Building Custom Collection class in PHP

Obada Alzidi
6 min readNov 3, 2024

--

Introduction:

A Collection class is an OOP-Replacement for the PHP array data structure, which provides a convenient wrapper for working with arrays of data. Collections offer a more structured approach to handling data, allowing for advanced operations such as filtering, mapping, reducing, and sorting with ease.

By encapsulating array manipulation logic within methods, Collections promote cleaner and more readable code, enhancing code maintainability and facilitating common array operations in a more expressive manner.

Implementation:

Let’s start by defining a Collection class.

<?php

class Collection {

public function __construct(protected array $elements = []) {

//
}
}
  • The protected array $elements = [] part is a constructor property promotion feature introduced in PHP 8.
  • With constructor property promotion, the $elements property is automatically created and initialized when an object of the Collection class is instantiated.

Now, we need a way to access our Collection objects as arrays, and that can be done by implementing the PHP ArrayAccessinterface.

The ArrayAccess interface requires the implementation of the following methods:

  1. offsetExists($offset): This method determines whether a given offset exists in the object.
  2. offsetGet($offset): This method retrieves the value at a specified offset.
  3. offsetSet($offset, $value): This method sets the value at a specified offset.
  4. offsetUnset($offset): This method unsets the value at a specified offset.
<?php

class Collection implements ArrayAccess{

public function __construct(protected array $elements = []) {

//
}

public function offsetExists(mixed $offset): bool {

return array_key_exists($offset, $this->elements);
}

public function offsetGet(mixed $offset): mixed {

return $this->offsetExists($offset)
? $this->elements[$offset]
: null;
}

public function offsetSet(mixed $offset, mixed $value): void {

is_null($offset)
? $this->elements[] = $value
: $this->elements[$offset] = $value;
}

public function offsetUnset(mixed $offset): void {

if($this->offsetExists($offset)) {

unset($this->elements[$offset]);
}
}
}

offsetExists($offset) Method:

  • This method checks if a given offset exists within the object.
  • array_key_exists($offset, $this->elements): It checks if the offset exists within the $elements property of the object.
  • Returns true if the offset exists in the elements array, false otherwise.

offsetGet($offset) Method:

  • This method retrieves the value at a specified offset.
  • It first checks if the offset exists by calling offsetExists($offset).
  • If the offset exists, it returns the corresponding value from the elements array; otherwise, it returns null.

offsetSet($offset, $value) Method:

  • This method sets the value at a specified offset.
  • If the offset is null, it means the value should be appended to the elements array.
  • If the offset is not null, it sets the value at the specified offset within the elements array.

offsetUnset($offset) Method:

  • This method removes the value at a specified offset.
  • It first checks if the offset exists by calling offsetExists($offset).
  • If the offset exists, it removes the value at that offset from the elements array using unset.

Next, we need to iterate through our Collection objects internally and that can be done by implementing the PHP Iteratorinterface.

The Iteratorinterface requires the implementation of the following methods:

current():

  • This method returns the current element in the iteration.

key():

  • This method returns the key of the current element in the iteration.

next():

  • This method moves the internal pointer to the next element in the iteration.

rewind():

  • This method resets the internal pointer to the first element in the iteration.

valid():

  • This method checks if the current position of the internal pointer is valid (i.e., if there is an element at the current position).
<?php

class CustomCollection implements ArrayAccess, Iterator {


public function __construct(protected array $elements = []) {

//
}

public function current(): mixed {

return current($this->elements);
}

public function key(): mixed {

return key($this->elements);
}

public function next(): void {

next($this->elements);
}

public function rewind(): void {

reset($this->elements);
}

public function valid(): bool {

return isset($this->elements[$this->key()]);
}

}

current() Method:

  • returns the current element of the internal $elements array using the current function.

key() Method:

  • returns the key of the current element in the internal $elements array using the key function.

next() Method:

  • advances the internal pointer of the $elements array to the next element using the next function.

rewind() Method:

  • resets the internal pointer of the $elements array to the first element using the reset function.

valid() Method:

  • checks if the current key is set in the $elements array, verifying if the current position of the internal pointer is valid by using isset.

Let’s now implement some array methods:

<?php

class Collection implements ArrayAccess, Iterator {


public function __construct(protected array $elements = [])
{
//
}

public function map(callable $callback): static {

$keys = array_keys($this->elements);

$items = array_map($callback, $this->elements, $keys);

return new static(array_combine($keys, $items));
}

public function filter(?callable $callback, int $mode = 0): static {

return new static(array_filter($this->elements, $callback, $mode));
}

public function reduce(callable $callback, mixed $initial = null): mixed {

return array_reduce($this->elements, $callback, $initial);
}

public function sum(?string $key = null) {

return $this->reduce(fn ($sum, $item) => $sum + (is_null($item) ? $item : $item[$key]), 0);
}

public function flip(): static {

return new static(array_flip($this->elements));
}

public function push(... $elements): static {

foreach($elements as $element) {

$this->elements[] = $element;
}

return $this;
}

}

map Method:

  • The map method applies a callback function to each element in the collection.
  • It retrieves the keys of the elements array.
  • It uses array_map to apply the provided callback function to each element along with its key.
  • Finally, it creates a new instance of Collection with the mapped elements.

filter Method:

  • The filter method filters elements in the collection based on a callback function.
  • It uses array_filter to filter the elements based on the provided callback function.

reduce Method:

  • The reduce method reduces the collection to a single value using a callback function.
  • It uses PHP’s array_reduce function to reduce the elements to a single value using the given callback function.
  • The initial value for reduction can be optionally provided.

sum Method:

  • The sum method calculates the sum of values in the collection, optionally by a specific key.
  • It uses the reduce method with a callback function that calculates the sum based on the specified key.
  • The initial value for the sum is set to 0.

flip Method:

  • The flip method exchanges keys with values in the collection.
  • It uses array_flip to flip the keys and values in the elements array.
  • It returns a new instance of Collection with the flipped elements.

push Method:

  • The push method takes a variable number of arguments using the ...$elements syntax, which allows you to pass in multiple elements to be added to the collection.
  • The method iterates over each element passed in the argument list.
  • For each element, it appends (pushes) the element to the end of the internal $elements array of the Collection object.
  • After adding all the elements, the method returns $this, which refers to the current instance of the Collection. This allows method chaining, where you can call multiple methods on the same object in a sequence.

Testing:

Let’s put our implementation to the test:

  1. Creating a New Collection Object:
<?php

$collection = new CustomCollection([1, 5, 10, -1, 11]);

2. Looping Over the Collection Object:

<?php

foreach($collection as $key => $value) {

echo $key . " => " . $value . PHP_EOL;
}

/* OUTPUT

0 => 1
1 => 5
2 => 10
3 => -1
4 => 11
*/

3. Using the map Method:

<?php

$c = $collection->map(function($element) {

return $element * 2;
});

/* OUTPUT

[2, 10, 20, -2, 22]
*/

4. Using the filter Method:

<?php

$c = $collection->filter(function($element) {

return $element > 5;
});


/* OUTPUT

[10, 11]
*/

5. Using the reduce Method:

<?php

$c = $collection->reduce(function($carry, $element) {

return $carry + $element;
}, 0);

/* OUTPUT

26
*/

6. Using the push Method:

<?php

$collection->push(12, 13, -2);

/* OUTPUT

[1, 5, 10, -1, 11, 12, 13, -2]
*/

Summary:

This Custom Collection class in PHP offers a versatile and efficient way to manage collections of elements with essential functionalities like mapping, filtering, reducing, and more. By providing methods for common data manipulation operations, such as transforming elements, selecting subsets based on criteria, and aggregating values, this class empowers developers to handle data with ease and flexibility. With the ability to chain methods and perform various operations on collections effortlessly, theCollection class streamlines data handling tasks, making it a valuable tool for efficient and structured data manipulation in PHP applications.

--

--

Responses (2)