The correct way to execute delegates or callbacks in PHP

I need to implement the following template in 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')); } ... } 

What is the right way to do this.

PS

I used call_user_func for this purpose and believe that it is or not, he was able to call private members of the class, but after several weeks of development, I found that it stops working. Whether this was a mistake in my code or was there something else that made me think that "call_user_func" might call the private functions of the class, I donโ€™t know, but now I am looking for a simple, fast and elegant way to safely call one private member class from another class. I'm currently looking for a closure, but I have problems with "$ this" inside the closure ...

+9
php callback delegates
source share
3 answers

Anyway, if anyone is interested, I found the only possible solution through ReflectionMethod. Using this method with Php 5.3.2 gives a performance penalty and is 2.3 times slower than calling the class directly, and only 1.3 times slower than the call_user_func method. Therefore, in my case, this is absolutely acceptable. Here is the code if anyone is interested:

 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; } } 
+4
source share

PHP callbacks are not like callbacks in most other languages. Typical languages โ€‹โ€‹are callbacks as pointers, while PHP represents them as strings. There is no โ€œmagicโ€ between the string or array() syntax and the call. call_user_func(array($obj, 'str')) syntactically the same as $obj->str() . If str is private, the call will fail.

You just have to make your event handler public. This has real semantic meaning, i.e. "Designed to be called from outside my class."

This implementation has other interesting side effects, for example:

 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' } 
+4
source share

Over time, new ways to achieve this appear. The PSR-14 is currently designed for use in this case.

So you can find any of these interesting ones: https://packagist.org/?query=psr-14

0
source share

Source: https://habr.com/ru/post/650626/


All Articles