The correct way of doing delegates or callbacks in PHP

Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.

You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."

This implementation choice 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'
}

Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone 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;
    }

}