我需要在php中实现以下模式:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
这样做的正确方法是什么。
P.S。
我为此目的使用call_user_func并且信不信由它能够调用私人类成员,但经过几周的开发后我发现它已经停止工作了。这是我的代码中的错误还是其他一些让我觉得'call_user_func'能够调用私有类函数的东西,我不知道,但现在我正在寻找一种简单,快速和优雅的安全方法从其他类调用一个私有类成员。我现在正在寻找封闭装置,但是封闭内部的'$ this'有问题......
答案 0 :(得分:4)
无论如何,如果有人感兴趣,我通过ReflectionMethod找到了唯一可能的解决方案。在Php 5.3.2中使用此方法会带来性能损失,并且比直接调用类成员慢2.3倍,并且只比call_user_func方法慢1.3倍。所以在我的情况下,这绝对是可以接受的如果有人感兴趣,这是代码:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
答案 1 :(得分:4)
PHP中的回调与大多数其他语言中的回调不同。典型语言将回调表示为指针,而PHP将它们表示为字符串。字符串或array()
语法与调用之间没有“魔力”。 call_user_func(array($obj, 'str'))
在语法上与$obj->str()
相同。如果str
是私有的,则呼叫将失败。
您应该简单地将事件处理程序设为公共。这具有有效的语义含义,即“打算从我的课外调用。”
此实现选择还有其他有趣的副作用,例如:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
答案 2 :(得分:0)
随着时间的流逝,有新的方法可以实现这一目标。 目前正在起草PSR-14来处理此用例。
因此,您可能会发现以下任何有趣的东西: https://packagist.org/?query=psr-14