PHP & event
¶
PHP syntax doesn't offer anything special for events. Most modern PHP libraries therefore implement something to provide such functionality. WordPress has its add_filter
and Symfony/Laravel have event listeners.
In .NET/C#, we have event
class members.
Goal¶
When extending a .NET application with PHP code, we would like to take advantage of C# events. It doesn't make sense to declare a new event
in PHP code, but it would be useful to register a PHP function to an existing event
.
Instead of the +=
operator used in C#, we need to take a different approach. Also, we need to alter the PeachPie/PHP runtime, so it all works seamlessly.
We decided to implement an API similar to TypeScript's events. We'll have a "fake" method add( callback )
on the event
class member, which returns a disposable object. Upon disposal, the registered callback
will be removed.
C# Example¶
In C#, event
(i.e. OnMessage
) is defined as depicted below:
In order to register a callback to the event, we use the +=
operator:
var obj = new MyListener();
obj.OnMessage += (sender, arg) => {
Console.WriteLine("Got a message!");
};
The event can be invoked by its owner only. This means only in the C# class:
class MyListener {
public event EventHandler OnMessage;
void GotMessage(string message) {
this.OnMessage(this, new MessageEventArgs(message));
}
}
Note: of course it's possible to use reflection to invoke an event outside its class context.
PHP Example¶
Having the same C# class MyListener
as in the example above, we can register a PHP callable as depicted below:
How It Works¶
The runtime knows that OnMessage
is not a property, but an event
. Before PeachPie version 1.2
, it simply ignored the event
class members. Now it sees them, but it fakes accessing them.
When the PeachPie runtime generates a read access, it returns an instance of ClrEvent
class (implemented in Peachpie.Runtime.dll
). This class has a method add( callback )
which converts the given callback
argument to a matching CLR delegate. Then, using .NET reflection, it registers it as an event handler of the original CLR event
.
The implementation (on GitHub) is straightforward.
It allows PHP code to register its callbacks to C# events without altering syntax, and C# code to invoke PHP functions without even noticing it's PHP. It all happens under the cover.
Unregistering Callback¶
Unregistering a callback cannot be done with something like remove( callback )
. Simply because the callback
is always a different object, and by default, the CLR event
remembers its registered callbacks as a list of delegate
instances.
PHP allows to have almost anything as a callable value (usually a string
or an array
or a class with the __invoke
method) - this is converted to a CLR delegate
instance - but always a new one - so removing such newly created delegate
would fail.
Instead, we chose the approach from the TypeScript language. add( callback )
returns a disposable object, which remembers the delegate
instance used originally, and safely unregisters that one upon disposal.
$obj = new MyListener;
$hook = $obj->OnMessage->add(function ($sender, $arg) {});
// remove the generated `delegate` to anonymous PHP `function`
// from the `OnMessage` event:
$hook->dispose();
Step Through the Code¶
Let's see how the debugging experience looks like when stepping through PHP code using C# events:
See More¶
Become a sponsor on Patreon¶
This new feature, and much more, is currently available only to our sponsors on Patreon. Please consider becoming a member to help us with the development of PeachPie and get early access to features such as this one, as well as release builds and nightly builds, and much more.