通过引用传递的对象中的数组是空的,即使它稍后被填充

In the progress of writing a little framework for a web app I came along some difficulties in making classes communicate with each other.

Environment

I have an abstract class called LizardModule, that should be extended by all the single modules of the web-app. This class has a final protected function registerController(...), that creates a new Object of the type LizardController. This is, as it sounds, based on the idea of MVC. With the final protected function registerFunction(...), modules can register functions for every controller. Those are stored using addFunction(...) on the controller object. Here is what this looks like:

Example Module:

class ModuleOverview extends LizardModule {
    protected function setup() {
        $this->registerController(
            'overview',
            'App Overview'
        );
        $this->registerFunction(
            'overview',
            'myfunction',
            'My Function',
            array(&$this, 'theFunctionToCall')
        );
    }

    public function theFunctionToCall() { ... Generate Content ... }
}

Module Class:

class LizardModule {
    private $controllers = array();

    final public function __construct() { $this->setup(); }

    abstract protected function setup();

    [...]

    final protected function registerController($controllerSlug, $controllerName) {
        if (array_key_exists($controllerSlug, $this->controllers))
            return false;

        $this->controllers[$controllerSlug] = new LizardController($controllerSlug, $controllerName);
    }

    final protected function registerFunction($controllerSlug, $functionSlug, $functionName, callable $function) {
        if (!array_key_exists($controllerSlug, $this->controllers))
            return false;

        $this->controllers[$controllerSlug]->addFunction($functionSlug, $functionName, $function);
    }
}

This results in a lot of objects of type LizardController in different places of the app. To make all of those objects accessable, I created a singleton class LizardRouter, that should hold a reference to all of those controller objects. Therefore, the controller-object registers itself with this singleton class:

Controller Class:

class LizardController {
    [...]
    private $functions = array();

    public function __construct($slug, $name, $menu) {
        $this->slug = $slug;
        $this->name = $name;
        $this->menu = $menu;

        LizardRouter::registerController($this);
    }

    public function addFunction(...) { Tested, this works. }

    public function getFunctions() {
        return $this->functions;
    }
}

Router Class:

final class LizardRouter {
    [...]

    public static function getControllers() {
        return static::getInstance()->controllers;
    }

    public static function registerController(LizardController $controller) {
        static::getInstance()->controllers[] = $controller;
    }
}

The Problem

The whole thing works alright for the controllers. In my interface class, I can read out all controllers and print a menu containing their names. The problem is: Whenever I access the controllers functions-array (see controller class) through the controllers-array given by the routing class, I get an empty array. I asume that somewhere a reference is not working and I am passing the actual controller object, before my module-class was able to add the functions to the controllers functions-array. But I can't figure out where exactly the problem lies. Here is an example from my interface class showing the problem:

foreach (LizardRouter::getControllers() as $controller) {

    // Allways returns an empty array, even though
    // the module added functions to the controller.
    $controller->getFunctions();
}

Since this is a very specific case, I guess it is unlikely, that anyone will ever stumble upon the same problem. Anyway; I found the reason for the problem:

Objects are by default passed as reference since PHP5. Variables are by default passed by value.

Arrays are handled like variables, so when I pass an array containing object-references, a new copy of this array is created and passed. Object references added to the array after it was passed are therefore only added to the original array.

The solution i chose was to create my own "array-class" for holding objects. It has nothing more than a private array object, a setter and a getter. Since this custom array class is an object, it is automatically passed by reference. Later I also added some functions to conveniently access the array - a good side-effect.