В ходе написания небольшой рамочной основы для веб-приложения я столкнулся с некоторыми трудностями в том, чтобы люди общались друг с другом.
У меня есть абстрактный класс LizardModule, который должен быть расширен всеми отдельными модулями веб-приложения. Этот класс имеет final protected function registerController(...)
, которая создает новый объект типа LizardController
. Это, как кажется, основано на идее MVC. С final protected function registerFunction(...)
модули могут регистрировать функции для каждого контроллера. Они сохраняются с помощью addFunction(...)
на объекте контроллера. Вот как это выглядит:
Пример:
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 ... }
}
Класс модуля:
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);
}
}
Это приводит к множеству объектов типа LizardController в разных местах приложения. Чтобы сделать доступными все эти объекты, я создал одиночный класс LizardRouter, который должен содержать ссылку на все эти объекты контроллера. Следовательно, объект-контроллер регистрируется с помощью этого одноэлементного класса:
Класс контроллера:
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;
}
}
Класс маршрутизатора:
final class LizardRouter {
[...]
public static function getControllers() {
return static::getInstance()->controllers;
}
public static function registerController(LizardController $controller) {
static::getInstance()->controllers[] = $controller;
}
}
Все это прекрасно работает для контроллеров. В моем классе интерфейса я могу считывать все контроллеры и печатать меню, содержащее их имена. Проблема в том, что всякий раз, когда я обращаюсь к функциям-массиву контроллеров (см. Класс контроллера) через массив-контроллеры, заданный классом маршрутизации, я получаю пустой массив. Я предполагаю, что где-то ссылка не работает, и я передаю фактический объект контроллера, прежде чем мой модуль-класс смог добавить функции к функциям-массиву контроллеров. Но я не могу понять, где именно лежит проблема. Вот пример из моего класса интерфейса, показывающий проблему:
foreach (LizardRouter::getControllers() as $controller) {
// Allways returns an empty array, even though
// the module added functions to the controller.
$controller->getFunctions();
}
Поскольку это очень конкретный случай, я предполагаю, что маловероятно, что кто-нибудь когда-либо наткнется на ту же проблему. Так или иначе; Я нашел причину проблемы:
Объекты по умолчанию передаются как ссылка с PHP5. Переменные по умолчанию передаются по значению.
Массивы обрабатываются как переменные, поэтому, когда я передаю массив, содержащий объектные ссылки, создается и передается новая копия этого массива. Ссылки на объекты, добавленные в массив после того, как они были переданы, поэтому добавляются в исходный массив.
Решение, которое я выбрал, состояло в том, чтобы создать собственный "массив-класс" для хранения объектов. Он имеет не что иное, как частный объект массива, сеттер и геттер. Поскольку этот пользовательский класс массива является объектом, он автоматически передается по ссылке. Позже я также добавил некоторые функции для удобного доступа к массиву - хороший побочный эффект.