Array Engine


Instantiation

The Zap\Core\Utils\ArrayEngine class is a fluent utility wrapper designed to simplify array manipulation, filtering, and transformation in PHP. It encapsulates a native array property and provides an extensive chainable API for array operations—ranging from basic element access (get(), has(), first(), last()) and mutation (push(), prepend(), merge(), sort(), except(), only()) to functional utilities (map(), filter(), reject(), reduce(), where()) and specialized data formatting like dot-notation nesting (dot(), undot(), getDot(), setDot()), string sanitization (escapeHtml(), trim()), and data aggregation (sum(), avg(), min(), max()).

To instantiate this utility class, you can either:

  • use it as a class normally; or
  • Retrieve it using Container as it has been registered to Container by default (see: /bootstrap/bindings.php).
use Zap\Core\Utils\ArrayEngine;
$ae = new ArrayEngine($array); //assuming that $array has been prepared before
$arrayData = $ae->all(); //all() returns the array

In the controller method, you can also inject it as a dependency.

use Zap\Core\Utils\ArrayEngine;
use Zap\Core\Utils\Container;

$ae = Container::getInstance()->make(ArrayEngine::class);
$arrayData = $ae->set($array)->all(); //set() sets the array to be processed later

If using the first is more convenient for you, you can remove the binding in the bindings.php. Once the class is instantiated, you can use it for various purposes.

Accessing and Retrieving Values

There are different ways to access and retrieve data from array using this utility class.

//basic key retrieval fallbacks
$status = $ae->get('status'); //returning the value of the key 'status'
$role = $ae->get('role', 'user'); //returning the value of the key 'role' if exists, or fallback to user if it does not exist

//fetching first and last item
$first = $ae->first();
$last = $ae->last();

//getting all keys or values only
$keys = $ae->keys()->all();
$values = $ae->values()->all();

//getting all values of specific keys from arrays
$userIds = $ae->pluck('id')->all();

Filtering and Querying Collections

ArrayEngine can be used to filter and query collections. Here are some practical examples showing how to filter and query collection data using this utility.

First, you can use where() for loose equality checks or whereStrict() when strict type matching is required.

//let's use new data
$users = new ArrayEngine([
 ['name' => 'Alice', 'role' => 'admin', 'active' => 1],
 ['name' => 'Bob',   'role' => 'editor', 'active' => 0],
 ['name' => 'Charlie', 'role' => 'admin', 'active' => 0],
]);
// Loose equality check (matches active == 1)
$activeUsers = $users->where('active', true)->all();

// Strict equality check
$admins = $users->whereStrict('role', 'admin')->all();

Second, you can filter data with whitelists and blacklists by using whereIn() or whereNotIn().

$posts = new ArrayEngine([
 ['id' => 1, 'status' => 'draft'],
 ['id' => 2, 'status' => 'published'],
 ['id' => 3, 'status' => 'archived'],
]);

// Keep items where status matches draft or published
$visiblePosts = $posts->whereIn('status', ['draft', 'published'])->all();

// Exclude archived items
$activePosts = $posts->whereNotIn('status', ['archived'])->all();

You can also use custom conditions for filtering and rejection. Use filter() to keep items that pass a callback condition, or reject() to remove items matching a condition.

$products = new ArrayEngine([
 ['title' => 'Keyboard', 'price' => 120, 'stock' => 15],
 ['title' => 'Mouse',    'price' => 45,  'stock' => 0],
 ['title' => 'Monitor',  'price' => 300, 'stock' => 8],
]);

// Keep items with price > 50
$expensive = $products->filter(fn($item) => $item['price'] > 50)->all();

// Reject items out of stock
$inStock = $products->reject(fn($item) => $item['stock'] === 0)->all();

To find first matching element or key, you can use find() to retrieve the first matching value, or findKey() to obtain its corresponding array key.

$accounts = new ArrayEngine([
 'acc_1' => ['name' => 'Sarah', 'verified' => false],
 'acc_2' => ['name' => 'David', 'verified' => true],
]);

// Returns ['name' => 'David', 'verified' => true]
$firstVerified = $accounts->find(fn($item) => $item['verified']);

// Returns 'acc_2'
$verifiedKey = $accounts->findKey(fn($item) => $item['verified']);

Finally, you can select or exclude specific keys by using only() or except() to filter dictionary keys in an associative array.

$payload = new ArrayEngine([
 'username' => 'johndoe',
 'password' => 'secret123',
 'email'    => 'john@example.com',
 'is_admin' => true,
]);

// Keep only specified keys
$profile = $payload->only(['username', 'email'])->all();

// Omit sensitive or restricted keys
$safePayload = $payload->except(['password', 'is_admin'])->all();

Inserting and Pushing Data

By using ArrayEngine, you can easily insert, append, and merge data. You can use add() to append a sequential scalar value or assign a value to a specific associative key.

$engine = new ArrayEngine(['alpha']);

// Append sequential value
$engine->add('beta'); // ['alpha', 'beta']

// Assign to a explicit key
$engine->add('role', 'editor'); // ['alpha', 'beta', 'role' => 'editor']

$result = $engine->all();

You can use push() with variadic arguments to append one or more elements to the end of the array.

$engine = new ArrayEngine(['Item 1']);

// Push multiple items in a single call
$result = $engine->push('Item 2', 'Item 3', 'Item 4')->all();
// ['Item 1', 'Item 2', 'Item 3', 'Item 4']

You can use prepend() to insert a new element at the very start of the array, shifting numerical indexes forward.

$engine = new ArrayEngine(['second', 'third']);

// Insert 'first' at index 0
$result = $engine->prepend('first')->all();
// ['first', 'second', 'third']

For merging arrays, use merge() to combine the current array with one or more external arrays. Numeric keys will be appended, while string keys with matching names will overwrite existing values.

$engine = new ArrayEngine([
 'theme' => 'light',
 'tags'  => ['php']
]);

// Merge with additional arrays
$result = $engine->merge(
 ['theme' => 'dark'],
 ['language' => 'en'], 
 ['tags' => ['framework', 'utility']]
)->all();
// Overwrites 'theme', Appends new 'language' key, Appends framework and utility to 'tags'

Finally, you can use replace() to recursively update or substitute matching keys from another array without appending duplicates.

$engine = new ArrayEngine([
 'status' => 'pending',
 'attempts' => 1
]);

$result = $engine->replace([
 'status' => 'completed',
 'attempts' => 2
])->all();

Transforming and Mapping Elements

Here are practical examples showing how to transform and map array contents using this utility class.

Use map() to transform every element in the array using a callback function.

$prices = new ArrayEngine([10, 20, 30]);

// Apply a 10% tax rate to all values
$taxedPrices = $prices->map(fn($price) => $price * 1.10)->all();
// [11.0, 22.0, 33.0]

You can also iterate over elements by using each() to run a function over every item—including key and value—without modifying the array structure.

$logs = new ArrayEngine(['error' => 'Disk full', 'warning' => 'High memory']);

$logs->each(function ($message, $level) {
 // Perform side-effects (e.g., write to log file or output text)
 echo "[$level]: $message\n";
});

You can also easily transform the case to lower or uppercase without having to iterate the entire array. Use lowercase() or uppercase() to recursively transform all string values within the array using multi-byte string operations.

$input = new ArrayEngine([
 'title' => 'Zap Framework',
 'tags'  => ['PHP', 'ROUTING']
]);

// Convert strings to lowercase
$lower = $input->lowercase()->all();
// ['title' => 'zap framework', 'tags' => ['php', 'routing']]

// Convert strings to uppercase
$upper = $input->uppercase()->all();
// ['title' => 'ZAP FRAMEWORK', 'tags' => ['PHP', 'ROUTING']]

Use reduce() to iteratively reduce an array to a single value using a callback function and optional initial seed.

$cart = new ArrayEngine([
 ['item' => 'Book', 'price' => 15],
 ['item' => 'Pen',  'price' => 3],
]);

// Calculate cumulative price
$totalPrice = $cart->reduce(function ($carry, $item) {
 return $carry + $item['price'];
}, 0); 
// Returns 18 directly (Value-returning method)

Finally, you can use flip() to swap all keys with their associated values.

$roles = new ArrayEngine([
 'admin' => 1,
 'editor' => 2,
]);

// Swap keys and values
$flipped = $roles->flip()->all();
// [1 => 'admin', 2 => 'editor']

Working with Dot-Notation

Here are practical examples showing how to manipulate multi-dimensional arrays using dot-notation path operations with ArrayEngine.

Use dot() to collapse multi-dimensional arrays into a single-level array where nested keys are joined by dots.

$config = new ArrayEngine([
 'database' => [
 'host' => '127.0.0.1',
 'credentials' => [
 'username' => 'root',
 'password' => 'secret'
 ]
 ]
]);

$dotted = $config->dot()->all();
/*
[
 'database.host' => '127.0.0.1',
 'database.credentials.username' => 'root',
 'database.credentials.password' => 'secret'
]
*/

Use undot() to perform the reverse transformation, converting dot-notated keys back into full nested multi-dimensional arrays.

$flat = new ArrayEngine([
 'app.name' => 'Zap Framework',
 'app.environment' => 'production',
 'db.connections.mysql.host' => 'localhost'
]);

$nested = $flat->undot()->all();
/*
[
 'app' => [
 'name' => 'Zap Framework',
 'environment' => 'production'
 ],
 'db' => [
 'connections' => [
 'mysql' => [
 'host' => 'localhost'
 ]
 ]
 ]
]
*/

Use getDot() to read values located deep within nested array structures using dot-separated key strings, with support for default fallbacks.

$settings = new ArrayEngine([
 'services' => [
 'mailgun' => [
 'domain' => 'mg.example.com',
 'secret' => 'key-12345'
 ]
 ]
]);

// Returns 'key-12345'
$secret = $settings->getDot('services.mailgun.secret');

// Returns 'smtp' fallback because key doesn't exist
$driver = $settings->getDot('services.driver', 'smtp');

Use setDot() to update or insert values into nested positions inside the array structure without destroying existing surrounding keys.

$settings = new ArrayEngine([
 'app' => ['name' => 'Zap']
]);

// Set nested path dynamically
$settings->setDot('app.services.cache.driver', 'redis');

$result = $settings->all();
/*
[
 'app' => [
 'name' => 'Zap',
 'services' => [
 'cache' => [
 'driver' => 'redis'
 ]
 ]
 ]
]
*/

Use flatten() to strip away all keys and collapse nested structures into a single flat list, or collapse() to reduce one level of array nesting.

$nestedList = new ArrayEngine([
 'fruits' => ['apple', 'banana'],
 'veggies' => ['carrot', ['spinach', 'kale']]
]);

// Collapse into a single flat numerical array
$flatList = $nestedList->flatten()->all();
// ['apple', 'banana', 'carrot', 'spinach', 'kale']

Cleaning and Sanitizing Input Data

Here are practical examples showing how to clean, sanitize, and remove empty or untrusted values using this class.

Use trim() to recursively strip leading and trailing whitespace from all string elements inside the array, including nested arrays.

$input = new ArrayEngine([
 'name' => '  Jane Doe  ',
 'bio'  => "  Software Developer\n  "
]);

$cleaned = $input->trim()->all();
// ['name' => 'Jane Doe', 'bio' => 'Software Developer']

Use sanitize() to recursively strip HTML/PHP tags and trim all strings across the array structure.

$userInput = new ArrayEngine([
 'title'   => '  

Welcome!

', 'comment' => '<script>alert("xss")</script>Hello World' ]); $safeInput = $userInput->sanitize()->all(); // ['title' => 'Welcome!', 'comment' => 'Hello World']

Use escapeHtml() to recursively convert special characters to HTML entities (using ENT_QUOTES | ENT_SUBSTITUTE by default) to prevent XSS vulnerabilities when rendering output.

$formData = new ArrayEngine([
 'bio'     => 'Me & You',
 'comment' => '<script>alert("xss")</script>'
]);

$escaped = $formData->escapeHtml()->all();
// [
//     'bio' => 'Me & amp; You',
//     'comment' => '& lt;script& gt;alert(& quot;xss& quot;)& lt;/script& gt;'
// ]
//extra space is added for code render purpose.

Use filterNull() to recursively remove all keys with null values while pruning any empty nested arrays left behind.

$data = new ArrayEngine([
 'id'       => 101,
 'nickname' => null,
 'profile'  => [
 'avatar' => null
 ]
]);

$filtered = $data->filterNull()->all();
// ['id' => 101]

Use filterEmpty() to recursively remove null elements, empty strings ('' or whitespace-only), and nested arrays that end up completely empty.

$requestData = new ArrayEngine([
 'title'   => 'Zap Framework',
 'summary' => '   ',
 'tags'    => []
]);

$cleanData = $requestData->filterEmpty()->all();
// ['title' => 'Zap Framework']

Use filterFalsy() to recursively purge all values evaluated as falsy in PHP (null, false, 0, "", []).

$flags = new ArrayEngine([
 'is_active' => true,
 'is_admin'  => false,
 'count'     => 0,
 'label'     => 'main'
]);

$activeFlags = $flags->filterFalsy()->all();
// ['is_active' => true, 'label' => 'main']

Sorting, Grouping, and Indexing Data

Here are practical examples showing how to sort, group, and re-index array collections using ArrayEngine

Use groupBy() to bucket array elements into sub-arrays based on a shared associative field name or a custom callback closure.

$employees = new ArrayEngine([
 ['name' => 'Alice',   'dept' => 'Engineering'],
 ['name' => 'Bob',     'dept' => 'Marketing'],
 ['name' => 'Charlie', 'dept' => 'Engineering'],
]);

// Group by string key
$byDept = $employees->groupBy('dept')->all();
/*
[
 'Engineering' => [
 ['name' => 'Alice', 'dept' => 'Engineering'],
 ['name' => 'Charlie', 'dept' => 'Engineering']
 ],
 'Marketing' => [
 ['name' => 'Bob', 'dept' => 'Marketing']
 ]
]
*/

// Group dynamically using a callback function
$byInitial = $employees->groupBy(fn($item) => $item['name'][0])->all();

Use keyBy() to re-index an array of associative arrays using the value of a specified field as the primary key.

$products = new ArrayEngine([
 ['sku' => 'PROD-A', 'price' => 20],
 ['sku' => 'PROD-B', 'price' => 50],
]);

$indexedBySku = $products->keyBy('sku')->all();
/*
[
 'PROD-A' => ['sku' => 'PROD-A', 'price' => 20],
 'PROD-B' => ['sku' => 'PROD-B', 'price' => 50]
]
*/

Use sortBy() or sortDescBy() to reorder an array of associative arrays based on a specified field's value.

$scores = new ArrayEngine([
 ['player' => 'Alex', 'points' => 350],
 ['player' => 'Sam',  'points' => 890],
 ['player' => 'John', 'points' => 120],
]);

// Sort ascending by points
$leaderboardAsc = $scores->sortBy('points')->all();

// Sort descending by points
$leaderboardDesc = $scores->sortDescBy('points')->all();

Use sort(), rsort(), or ksort() to reorder simple arrays or sort associative array keys.

$numbers = new ArrayEngine([5, 2, 8, 1]);

// Standard ascending value sort
$ascending = $numbers->sort()->all(); // [1, 2, 5, 8]

// Reverse descending value sort
$descending = $numbers->rsort()->all(); // [8, 5, 2, 1]

$mapped = new ArrayEngine(['z' => 1, 'a' => 2, 'm' => 3]);

// Sort by associative keys
$keySorted = $mapped->ksort()->all(); // ['a' => 2, 'm' => 3, 'z' => 1]

Use shuffle() to randomly reorder all elements within the array.

$cards = new ArrayEngine(['Ace', 'King', 'Queen', 'Jack']);
$shuffled = $cards->shuffle()->all();

Computing Aggregates Statistics

Here are practical examples showing how to calculate aggregates, totals, and statistical summaries using ArrayEngine.

Note: All aggregate methods in this section return scalar values (int, float, or direct elements) directly, so you do not call all() or final() after them.

Use sum() without parameters for simple numerical arrays, or pass a key name to total up a specific property across an array of associative arrays.

// Summing simple numeric values
$scores = new ArrayEngine([10, 20, 30, 40]);
$totalScore = $scores->sum(); // Returns 100

// Summing specific keys across records
$order = new ArrayEngine([
 ['item' => 'Widget A', 'price' => 29.99],
 ['item' => 'Widget B', 'price' => 15.00],
 ['item' => 'Widget C', 'price' => 5.00],
]);

$totalPrice = $order->sum('price'); // Returns 49.99

Use avg() to compute the arithmetic mean of a flat numeric array or a targeted column in a collection. It automatically handles empty array checks to prevent division-by-zero errors.

$metrics = new ArrayEngine([
 ['response_time' => 120],
 ['response_time' => 80],
 ['response_time' => 250],
]);

// Returns 150.0
$averageTime = $metrics->avg('response_time');

Use min() and max() to extract the lowest or highest scalar value from an array, or to evaluate specific record properties.

$temperatures = new ArrayEngine([22.5, 18.0, 31.2, 27.4]);

$lowest  = $temperatures->min(); // Returns 18.0
$highest = $temperatures->max(); // Returns 31.2

$employees = new ArrayEngine([
 ['name' => 'Alice', 'salary' => 75000],
 ['name' => 'Bob',   'salary' => 92000],
 ['name' => 'Carol', 'salary' => 64000],
]);

// Extract min/max values by column key
$minSalary = $employees->min('salary'); // Returns 64000
$maxSalary = $employees->max('salary'); // Returns 92000

Use count() to quickly determine the total number of top-level elements in the collection.

$items = new ArrayEngine(['apple', 'banana', 'cherry']);

$totalItems = $items->count(); // Returns 3

Checking Condition and Validation

Here are practical examples showing how to validate array state and test condition callbacks using ArrayEngine.

Note: All methods in this section return boolean true or false directly, so you do not call all() or final() after them.

Use isEmpty(), has(), or contains() to check if the collection is empty, whether a specific key exists, or if a value exists within the array.

$settings = new ArrayEngine(['theme' => 'dark', 'debug' => true]);

// Check if array has no elements
$empty = $settings->isEmpty(); // false

// Check key existence
$hasTheme = $settings->has('theme'); // true

// Check value existence (optional strict comparison flag)
$hasDark = $settings->contains('dark', strict: true); // true

Use every() to determine if all elements in the array satisfy a given callback condition.

$users = new ArrayEngine([
 ['id' => 1, 'verified' => true],
 ['id' => 2, 'verified' => true],
 ['id' => 3, 'verified' => true],
]);

// Returns true only if ALL items pass the condition
$allVerified = $users->every(fn($user) => $user['verified'] === true); // true

Use some() to verify if at least one element satisfies a given condition.

$orders = new ArrayEngine([
 ['id' => 101, 'status' => 'shipped'],
 ['id' => 102, 'status' => 'pending'],
 ['id' => 103, 'status' => 'shipped'],
]);

// Returns true if AT LEAST ONE item matches
$hasPending = $orders->some(fn($order) => $order['status'] === 'pending'); // true

Use none() to confirm that no elements in the collection satisfy a condition.

$inventory = new ArrayEngine([
 ['sku' => 'A1', 'stock' => 5],
 ['sku' => 'B2', 'stock' => 12],
]);

// Returns true because NO item has stock equal to 0
$noneOutOfStock = $inventory->none(fn($item) => $item['stock'] === 0); // true

Use equal() for loose structural equality checks or identical() for strict value and type comparisons against another array.

$a = new ArrayEngine(['id' => '10', 'role' => 'admin']);

// Loose check (equal content, loose type comparison)
$isEqual = $a->equal(['id' => 10, 'role' => 'admin']); // true

// Strict check (matching types required)
$isIdentical = $a->identical(['id' => 10, 'role' => 'admin']); // false

Slicing, Splitting, and Chunking Arrays

Here are practical examples showing how to slice, split, and chunk array collections using ArrayEngine.

Use slice() to extract a specific subset of elements starting at an offset, optionally restricting the total length returned.

$items = new ArrayEngine(['a', 'b', 'c', 'd', 'e', 'f']);

// Extract 3 elements starting from index 2
$subset = $items->slice(2, 3)->all();
// ['c', 'd', 'e']

Use chunk() to break a single array into multiple smaller arrays of a designated size.

Note: chunk() returns a raw array of arrays directly, so you do not call all() or final() after it.
$records = new ArrayEngine([1, 2, 3, 4, 5, 6, 7]);

// Split into arrays of at most 3 items
$batches = $records->chunk(3);
/*
[
 [1, 2, 3],
 [4, 5, 6],
 [7]
]
*/

Use partition() to separate array elements into two distinct groups based on a truth test callback function. It returns a two-element array where index 0 contains items that passed the condition and index 1 contains items that failed.

Note: Like chunk(), partition() returns an array pair directly, so all() or final() is not required.
$numbers = new ArrayEngine([10, 15, 20, 25, 30]);

// Separate even numbers from odd numbers
[$evens, $odds] = $numbers->partition(fn($num) => $num % 2 === 0);

// $evens: [0 => 10, 2 => 20, 4 => 30]
// $odds:  [1 => 15, 3 => 25]

Exporting and Resetting Internal State

Here are practical examples showing how to export, clear, re-assign, and reset the internal state of ArrayEngine.

Use all() or final() to finalize your method chain and extract the current internal PHP array state.

$engine = new ArrayEngine(['alpha', 'beta']);

$engine->push('gamma')->trim();

// Retrieve the underlying raw PHP array
$rawArray = $engine->all(); // ['alpha', 'beta', 'gamma']

// final() acts as an exact alias to all()
$finalArray = $engine->final(); // ['alpha', 'beta', 'gamma']

Use implode() to convert array elements into a single glued string separated by a delimiter.

Note: implode() returns a string directly, so you do not call all() or final() after it.
$tags = new ArrayEngine(['php', 'framework', 'zap']);

// Returns 'php, framework, zap'
$tagString = $tags->implode(', ');

Use set() to replace the entire internal array with a brand-new dataset while keeping the same ArrayEngine instance.

$engine = new ArrayEngine(['initial' => 'data']);

// Re-assign internal state fluently
$engine->set([
 'user_id' => 42,
 'status'  => 'active'
]);

$data = $engine->all(); // ['user_id' => 42, 'status' => 'active']

Use clear() to purge all elements from the array, resetting the internal state back to an empty array ([]).

$engine = new ArrayEngine(['temp_1', 'temp_2', 'temp_3']);

// Empties the array fluently
$emptyData = $engine->clear()->all(); // []

Miscellaneous Operations

Here are practical examples for the remaining Miscellaneous Operations using ArrayEngine.

Use removeKey() to unset an element by its associative or numerical key, or removeValue() to filter out all occurrences of a specific value.

$payload = new ArrayEngine([
 'username' => 'johndoe',
 'password' => 'secret123',
 'status'   => 'draft',
 'type'     => 'draft'
]);

// Remove a specific key
$payload->removeKey('password');

// Remove all matching values (loose comparison by default)
$cleaned = $payload->removeValue('draft')->all();
// ['username' => 'johndoe']

Use search() to locate the key of a given value.

Note: search() returns the matching key or false directly, so all() or final() is not required.
$roles = new ArrayEngine([
 'user_101' => 'admin',
 'user_102' => 'editor'
]);

// Returns 'user_101'
$adminKey = $roles->search('admin');

// Returns false
$guestKey = $roles->search('guest');

Use unique() to strip duplicate values from the array, preserving original keys.

$tags = new ArrayEngine(['php', 'web', 'php', 'backend', 'web']);

$uniqueTags = $tags->unique()->all();
// ['php', 'web', 'backend']

Use diff() to keep values not present in a given comparison array, or intersect() to retain only values common to both arrays.

$userPermissions = new ArrayEngine(['read', 'write', 'execute', 'delete']);

// Keep permissions NOT in the restricted list
$allowed = $userPermissions->diff(['execute', 'delete'])->all();
// ['read', 'write']

// Keep only permissions that overlap with the required set
$overlapping = $userPermissions->intersect(['read', 'export'])->all();
// ['read']

Use reverse() to invert the order of elements in the array.

$steps = new ArrayEngine(['Step 1', 'Step 2', 'Step 3']);

$reversed = $steps->reverse()->all();
// ['Step 3', 'Step 2', 'Step 1']