Avoid Dynamic Data structures in PHP

Published in category Programming
on Christian Mayer's Weblog.

This blog post is an object-oriented approach to use Classes instead of build-in arrays. I’ll show you another view on why it’s better to use a Class/Object instead of an array.

In the PHP world it’s very common to use build-in arrays ([] or array()) for every purpose. It’s PHP’s Swiss Army Knife. But doesn’t look as nice as your wife.

Let’s jump into the code. Consider you have a very long and complex code block. For the purpose of this blog post I’ll make it only 2 levels deep:

foreach (/* ... */) {
  foreach (/* ... */) {
    // Collect Data
  }
}

Outside of this block we have an array which collects some data from the inner loops for later use. Let’s concentrate only on the collecting block. Most PHP programmers would now do something like this:

$collection = []; // For later use.
foreach (/* ... */) {
  $widget = [];
  foreach (/* ... */) {
    $widget = ...;
  }
  $collection[] = $widget;
}
// Further use of $collection.

I’ve seen and self written PHP code like this. It’s very easy to end up with a data structure like this:

$collection = [
  [
    'order' => (int),
    'original' => (object),
    'info' => [
      'message' => (string),
      'created_at' => (DateTime),
      ...
    ],
    ...
  ],
  ...
];

This dynamic structure is bad. Later when you (or someone else) have to extend it, you are not sure anymore how the data structure looks like. You have to look up every write position to be sure what you are changing.

So better use Classes here. With Classes you cannot create a dynamically growing data structure. Your structure is fixed. But this approach is not every common in the PHP world. Everyone uses build-in arrays.

But in our example from above it’s better to use Classes. Instead of using a build-in array we now change it to objects.

class Collection {
    /** @var Widget[] */
    private $widgets;

    public function __construct() {
        $this->widgets = [];
    }

    /** @return Widget[] */
    public function getWidgets(): array {
        return $this->widgets;
    }

    /** @param Widget[] $widgets */
    public function setWidgets(array $widgets): void {
        $this->widgets = $widgets;
    }

    /** @param Widget $widget */
    public function addWidget(Widget $widget) {
        $this->widgets[] = $widget;
    }
}

Ok, so now we still use an array for the widgets, but at least it’s wraped in a class. Be sure not to miss the initialization of the $widgets in the constructor. For arrays inside a class there is also an add() method needed. In this example it’s called addWidget().

Next, create the Widget class:

class Widget {
    /** @var int */
    private $order;

    /** @var SomeObject */
    private $original;

    /** @var Info */
    private $info;

    // More properties here.
    ...

    public function __construct()
    {
        $this->order = 0;
    }

    // Getters and Setters here.
    ...
}

The same goes for the info field: create an Info class.

class Info {
    /** @var string */
    private $message;

    /** @var \DateTime */
    private $createdAt;

    // More properties here.
    ...

    // Getters and Setters here.
    ...
}

Now we change our loop to use objects.

$collection = new Collection();
foreach (/* ... */) {
  $widget = new Widget();
  foreach (/* ... */) {
    $widget->...;
  }
  $collection->addWidget($widget);
}

Conclusion

Instead of using a sub-array of an array we create a separate class. You will end up having more classes than usually. This is fine. Just make sure to use a separate namespace for the data classes. \App\Data would be good.

The examples in this post are an approach to use clean data structures in your PHP application. It aims to be easy maintainable, also for the long-run, by avoiding dynamic data structures. Sometimes you can only use build-in arrays, but when it’s possible to avoid them, you should.

Actually C++ inspired me for this. Since I’m doing more C++ recently I got different views on PHP topics too. In C++ you don’t have something like a general-purpose array which can hold everything, randomly. You have to know forefront which data type you want to use. Using new programming languages will bring you new insights and ideas to already known languages. You get a better understanding.

Recent Posts

About the Author

Christian is a professional software developer living in Vienna, Austria. He loves coffee and is strongly addicted to music. In his spare time he writes open source software. He is known for developing automatic data processing systems for Debian Linux.