Collection Classes in PHP

Alireza Rahmani Khalili
Tweet

A Collection class is an OOP replacement for the traditional array data structure. Much like an array, a collection contains member elements, although these tend to be objects rather than simpler types such as strings and integers.

The general characteristics of a collection class are:

  • Establishes a wrapper around a array of objects.
  • Collections are mutable – new elements may be added and existing elements may be modified or removed.
  • Sorting algorithms are unstable (which means the order for equal elements is undefined).
  • Can use lazy instantiation to save system resources.

Problem with Arrays

Applications frequently have objects that contain a group of other objects, and this is a great place to make use of collections. For example, suppose we decide to create a book store system. Let’s say we’ve written a customer class which, among other things, holds a list of books that the customer would like to purchase:

<?php
class Customer 
{
    public $items = array();
    // ...
}

$customer = new Customer(1234);
foreach ($customer->items as $item) {
    echo $item->name;
}

If the most obvious approach (using an array) were the best possible approach, I wouldn’t write this article. The above example has these problems:

  • We’ve broken encapsulation – the array is exposed as a public member variable.
  • There is ambiguity in indexing and how to traverse the array to find a specific item.

Additionally, to make sure the array is available for any code that might access it, we must populate the list of information from the database at the same time as the customer information. This means that even if we want to print just the customer’s name, we must fetch all of the item information, unnecessarily increasing load on the database and potentially bogging down the entire application.

We can solve those problems by creating a collection class as an OOP wrapper around the array and use lazy instantiation. Lazy instantiation is the mechanism by which we create elements in the array only when we actually need it. It’s called “lazy” because the object determines on its own when to instantiate the component objects rather that blindly creating them when it is instantiated.

A Basic Collection Class

A collection class needs to expose methods that allow us to add, retrieve, and delete items, and it’s helpful to have a method that lets us know the size of the collection. So, a basic class would start like this:

<?php
class Collection 
{
    private $items = array();

    public function addItem($obj, $key = null) {
    }

    public function deleteItem($key) {
    }

    public function getItem($key) {
    }
}

The $items array provides a location in which to store the objects that are members of the collection. addItem() lets us add a new object to the collection, deleteItem() removes an object, and getItem() returns an object.

With addItem(), we add an object to the collection by putting it in the $items array at a specified location specified by $key (if no key is provided, we let PHP pick the next available index). If an attempt is made to add an object using a key that already exists, an exception should be thrown to prevent inadvertently overwriting existing information:

public function addItem($obj, $key = null) {
    if ($key == null) {
        $this->items[] = $obj;
    }
    else {
        if (isset($this->items[$key])) {
            throw new KeyHasUseException("Key $key already in use.");
        }
        else {
            $this->items[$key] = $obj;
        }
    }
}

The deleteItem() and getItem() methods take the key as a parameter indicating which items are targeted for removal or retrieval. An exception should be thrown if an invalid key is supplied.

public function deleteItem($key) {
    if (isset($this->items[$key])) {
        unset($this- >items[$key]);
    }
    else {
        throw new KeyInvalidException("Invalid key $key.");
    }
}

public function getItem($key) {
    if (isset($this->items[$key])) {
        return $this->items[$key];
    }
    else {
        throw new KeyInvalidException("Invalid key $key.");
    }
}

Because the $key parameter to the addItem() method is optional, we won’t necessarily know the key used for each item in the collection. Adding a method that can provide a list of keys to any external code that might need it is a good idea. The keys can be returned as an array:

public function keys() {
    return array_keys($this->items);
}

It might also be helpful to know how many items are in the collection.

public function length() {
    return count($this->items);
}

And because getItem() and deleteItem() can throw an exception if an invalid key is passed, a means of determining whether a given key exists in the collection is also a good idea.

public function keyExists($key) {
    return isset($this->items[$key]);
}

To use the Collection class as it stands now, create the file Collection.php and save the code for the Collection class in it. Create files for the KeyInvalidException and KeyhasUseException classes, too (they can be simple sub-classes of the base Exception class). Be sure to add require statements if you’re not using an autoloader, and then try the following test code:

<?php
require_once "Collection.php";

class Salut
{
    private $name;
    private $number;

    public function __construct($name, $number) {
        $this->name = $name;
        $this->number = $number;
    }

    public function __toString() {
        return $this->name . " is number " . $this->number;
    }
}

$c = new Collection();
$c->addItem(new Salut("Steve", 14), "steve");
$c->addItem(new Salut("Ed", 37), "ed");
$c->addItem(new Salut("Bob", 49), "bob");

$c->deleteItem("steve");

try {
    $c->getItem("steve");
}
catch (KeyInvalidException $e) {
    print "The collection doesn't contain Steve.";
}

This example may not be particularly interesting, but it should give you an idea of how the class is used.

Conclusion

Collections can be seen as more-specialized way of working with lists for which certain contracts are guaranteed. A Collection class is a very useful OO alternative to the traditional array, and one that can be implemented in virtually any application you might build. It provides careful management of its members and a consistent API that makes it easy to write code that uses the class.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Anonymous

    If you’re going to build a custom Collection, it may be useful to use it as an Array. For more information on Array objects in PHP, see the ArrayAccess interface.
    http://php.net/manual/en/class.arrayaccess.php

  • Chris Emerson

    Not a single mention of implementing the Iterator or Countable, or even ArrayAccess interfaces to make it far more usable? This implementation looks really kludgy and much harder to use – you can’t just foreach over it for example, or use count() on it!

  • Anonymous

    Unless I’m misunderstanding, you switch from using the array $items to an array named $members. Is that a typo? If not, shouldn’t you declare the visibility of the $members array?

  • Anonymous

    What about the SPL ?!?!

    • Alireza Rahmani

      in tutorials we are meant to describe “how to create” not “how use use”;if there was no SPL what would you do ??!
      if we have different idea or the project obliged us to behave differently isn’t necessary to know the structure??!!

      • Anonymous

        You just taught how to build a very poorly designed collection class. We should not think about stuff that is not real. In your case: SPL do exist.

  • Anonymous
    • Alireza Rahmani

      dear here we talk about structure and focus on how to create not how to use !!

      • Pino

        You are not talking about ‘how to create’ but ‘how to reinvent a square wheel’.

  • marakoss

    Hi there, I maybe misunderstood the article, but can someone point the main benefit of doing this? All I see is that you figure out, how to complicate your life as a programmer. How do you manage to do such things like array_merge, array_diff? Working with multidimensional “arrays” (collections) is now pain in the ass, because you need to assign the lowest possible child first and then work your way up. And doing some sorting (without adding metods that do sort, uasort, uksort, usort, etc…) is impossible, without making the $items public – which takes you to the start of the problem.

    MK

    • Alireza Rahmani

      did u read “The general characteristics of a collection class”? then using sort or this kinda code wouldn’t give the required performance!

  • bradym

    Good writeup. In the third code sample block, you reference $this->members a couple times. That should be $this->items.

  • Anonymous

    Guys, I don’t mean to be a pain or something, but really, what is the real benefit of this implementation based on Object Oriented Design ? We should pay attention to the language itself, which had major improvements with the advent of SPL and aforementioned classes such as ArrayObject, or interfaces such as ArrayAccess. We should help the community with articles that really make developers think about how to make the code they write better and better. How about writing articles on Test Driven Development, or tools that help build software using this approach, such as Mockery, Hamcrest or Codeception ? Despite some very good articles on OOD, mainly written by Alejandro Gervasio here, it’s all about re-implementing things that other (many) experienced developers already have implemented in the language itself ! We should help the community with much more than that.

  • mark

    I have put this type of class into practice many times and have found it useful. The Collection class is a good place to put universal functionality that is independant of the object being stored, like sorting by named properties, min, max, etc.. There may be existing api calls that come close to what’s needed, but eventually you’ll need to go beyond the basics provided.
    The one problem I see with the demo is that submitting an object to the dictionary without a key value will cause the Collection container to store an object that is not retrievable. Better to force the use of a key, explicity provided or generated by some method in the Collection class.

    • Alireza Rahmani

      it would be Good idea thanks for sharing the experience.

  • Anonymous

    An example of better written Collection class from the Laravel framework. Look at the methods, you will see the benefits immediatly : https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Collection.php

    • Alireza Rahmani

      dude we are talking about frame works and loading heavy libraries! just for simple action! we just describe “how to”

  • tot

    The intro said about lazy loading yet it wasn’t followed by any examples. It would me more useful to get some practical examples of how such a data structure can be used, what design pattern would it fit, what problems does it solve? How is this better than what SPL already offers? What I see here is one paragraph of wiki-style write-up on the idea and a thin and debatable implementation. How about expanding this piece by things mentioned in the comments above, so that a quote “This example may not be particularly interesting, but it should give you an idea of how the class is used.” wouldn’t also be a best summary of this article?

    • Alireza Rahmani

      we are not talking about design pattern if we talk we would describe iterator pattern or other solutions; there is no necessity to write lazy loading code just i noticed the benefits if there would be lazy loading ; the article should cover all notices and it will be long and confusing, about the SPL here we show a simple and light code to work with array which may be cause of problem in OOP development if we want to include library there would be several ways.

  • Pino

    quote: “we must populate the list of information from the database at the same time as the customer information” Wow, bad design alert! If that is problem, then your ‘collection’ class isn’t the solution.

    • Alireza Rahmani

      it’s one of the problems not the only!

  • Anonymous

    Interesting article.
    I can see how a collection object would be preferable to a simple array in some situations. It might be fun to add a __invoke method as well depending on the type of collection and how it will be used.
    Thanks for sharing.

    • Alireza Rahmani

      thanks for comment

  • Anonymous

    I tought you will mention ArrayAccess, Iterator and stuff.

    • Alireza Rahmani

      if you are interested in suggest “expert php” by wrox!

  • Radek

    Check out the SplObjectStorage – it’s already implemented in PHP and it implements interfaces Countable , Iterator , Serializable , ArrayAccess. Good luck! http://php.net/manual/en/class.splobjectstorage.php

  • Anonymous

    this is kinda simple, and you would maybe do like this before the built-in interfaces like Radek wrote.
    Look at laravels implementation of a collection class: http://laravel.com/api/source-class-Illuminate.Support.Collection.html

  • Anonymous

    where is the lazyloading part of this tutorial?

  • Ded Moroz

    насамам деели ачиридной гавнакод исчо аднаво эндуса. Awesome man. you rook!

  • Antonis Adamakos

    I would stick with SPL classes, especiallty ArrayAccess :)

  • Gabriel
  • Anonymous

    Just extend ArrayObject, it provides everything needed to use the collection has if it was an array (IteratorAggregate, ArrayAccess, Serializable and Countable) and you can implement your own logics on top of it.

  • Sobit

    Poor article which even doesn’t show up hot to use ArrayAccess and others. What is the purpose of such Collection class? Too bad php[architect] included this article in their monthly subscription.