php-standard-library/vec
php-standard-library/vec provides small, focused helpers for working with sequential 0-indexed arrays (lists). Create, map, filter, transform, and compose list operations with predictable behavior and clean APIs—part of the PHP Standard Library collection.
Installation:
composer require php-standard-library/vec
No additional configuration is needed beyond Composer autoloading.
First Use Case:
Replace a complex array manipulation with a Vec pipeline. For example, transform an array of user data into a formatted list:
use PhpStandardLibrary\Vec;
$users = Vec::fromArray([
['name' => 'John', 'age' => 30],
['name' => 'Jane', 'age' => 25],
]);
$formattedNames = $users->map(fn($user) => "{$user['name']} ({$user['age']})");
// Returns Vec(['John (30)', 'Jane (25)'])
Where to Look First:
Vec::fromArray(), map(), filter(), reduce(), and pipe() for most use cases.Vec in service layers or DTOs where raw arrays are currently used.Use Vec for declarative, chainable operations in Laravel services or controllers:
// In a Laravel service handling API responses
public function processOrders(array $rawOrders): Vec
{
return Vec::fromArray($rawOrders)
->filter(fn($order) => $order['status'] === 'pending')
->map(fn($order) => [
'id' => $order['id'],
'total' => $order['total'] * 1.1, // Apply tax
])
->sort(fn($a, $b) => $a['total'] <=> $b['total']);
}
Leverage Vec for immutable transformations in domain logic:
// In a Laravel command or job
public function handle()
{
$initialState = Vec::of(['step1', 'step2', 'step3']);
$updatedState = $initialState->push('step4'); // Returns new Vec, original unchanged
// ...
}
Bridge Vec and Laravel’s Collection where needed:
// Convert Vec to Collection for Laravel-specific methods
$vec = Vec::fromArray([1, 2, 3]);
$collection = collect($vec->toArray());
// Or use Vec for domain logic, then convert back
$filteredVec = $vec->filter(fn($n) => $n > 1);
$filteredCollection = collect($filteredVec->toArray());
Extend Vec with custom methods for project-specific needs:
// Add a custom method to Vec (e.g., in a helper trait)
Vec::macro('toCsv', function(): string {
return implode(',', $this->map(fn($item) => (string)$item));
});
// Usage
$vec = Vec::of([1, 2, 3]);
echo $vec->toCsv(); // "1,2,3"
chunkUse Vec::chunk() for batch operations (e.g., processing large datasets):
// In a Laravel job or command
$largeDataset = Vec::fromArray(range(1, 1000));
$chunks = $largeDataset->chunk(100);
foreach ($chunks as $chunk) {
// Process each chunk (e.g., save to DB in batches)
DB::table('results')->insert($chunk->toArray());
}
Enforce type hints for better IDE support and clarity:
/**
* @param Vec<int, User> $users
* @return Vec<string>
*/
public function getUserNames(Vec $users): Vec
{
return $users->map(fn(User $user) => $user->name);
}
Non-Indexed Arrays:
Vec only works with 0-indexed arrays. Passing associative arrays will throw errors:
// ❌ Throws exception
Vec::fromArray(['id' => 1, 'name' => 'Alice']);
Fix: Use array_values() or Vec::fromArray(array_values($assocArray)).
Performance Overhead:
While minimal, Vec methods may be slightly slower than native arrays for simple operations. Benchmark critical paths:
// Compare Vec vs. native array for your use case
$vec = Vec::fromArray(range(1, 10000));
$array = range(1, 10000);
// Time Vec::map vs. array_map
Laravel Collection Confusion:
Avoid mixing Vec and Collection methods in the same pipeline. For example:
// ❌ Ambiguous (which `map` is called?)
Vec::fromArray([1, 2, 3])->map(fn($n) => $n * 2)->pluck('key'); // Error
Fix: Stick to one type per pipeline or explicitly convert.
Immutability Misconceptions:
Vec methods like push() or pop() return new instances but do not modify the original. This can lead to unexpected behavior if not handled carefully:
$vec = Vec::of([1, 2]);
$newVec = $vec->push(3);
// $vec is still [1, 2], $newVec is [1, 2, 3]
Debugging:
Vec instances are not natively debug-friendly. Override __toString() for better logging:
Vec::macro('__toString', function(): string {
return 'Vec(' . implode(', ', $this->toArray()) . ')';
});
Use toArray() for Inspection:
Convert Vec to an array for debugging:
dd($vec->toArray()); // Instead of dd($vec)
Custom Error Handling: Add validation for invalid inputs:
Vec::macro('ensureIndexed', function(): void {
if (!array_keys($this->toArray()) === range(0, count($this) - 1)) {
throw new \InvalidArgumentException('Vec must be 0-indexed');
}
});
Logging:
Log Vec operations for tracing:
$vec->pipe(function($vec) {
logger()->debug('Vec state:', ['data' => $vec->toArray()]);
return $vec;
});
No Laravel Service Provider:
Vec does not include a Laravel service provider or facade. Register it manually if needed:
// In AppServiceProvider
app()->singleton('vec', fn() => new Vec());
Composer Autoloading:
Ensure Vec is autoloaded by Composer. If using a monorepo or custom setup, verify:
// composer.json
{
"autoload": {
"psr-4": {
"PhpStandardLibrary\\Vec\\": "vendor/php-standard-library/vec/src"
}
}
}
Custom Methods:
Add project-specific methods using Vec::macro():
Vec::macro('sum', function(): int {
return $this->reduce(0, fn($sum, $item) => $sum + $item);
});
Immutable Data Structures:
Extend Vec to support persistent data structures (e.g., for functional programming):
Vec::macro('with', function($index, $value) {
$copy = clone $this;
$copy->set($index, $value);
return $copy;
});
Integration with Laravel: Create a facade for convenience:
// app/Facades/VecFacade.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
use PhpStandardLibrary\Vec;
class VecFacade extends Facade {
protected static function getFacadeAccessor() { return Vec::class; }
}
Testing Utilities: Add helper methods for testing:
Vec::macro('assertEquals', function(Vec $expected): void {
if (!$this->equals($expected)) {
throw new \PHPUnit\Framework\AssertionFailedError(
"Vecs not equal. Expected: " . $expected->toArray() .
", Actual: " . $this->toArray()
);
}
});
array_push), native arraysHow can I help you explore Laravel packages today?