loading...
Cover image for Working with events in neoan3 (PHP)

Working with events in neoan3 (PHP)

sroehrl profile image neoan ・3 min read

Events are usually something I make use of in JavaScript. After all, PHP classically isn't very asynchronous and regardless of how object oriented you work, at the end you just abstract the procedural thread.

Observations

However, when working with neoan3 you notice a couple of unusual patterns. Especially when using the built-in REST API, you often make use of the custom "RouteException":

component/item/Item.ctrl.php

...
function getItem($item)
{
   if (!isset($item['id'])) {
       throw new RouteException('Missing field "id"', 400);
   }

   return ItemModel::byId($item['id']);
}

What is happening here?

If we do not provide an id the API responds with a well-formed error and a 400 header. Let's think about this: Prior to our "item"-endpoint, the API apparently tries to execute our code and catches the Exception in order to react. So in a way, we are providing previously executed code input. This is not too unusual from the standpoint of architecture, but it has it's limits:

What if I want to execute custom behavior?

Recently I needed to implement CORS policy to allow certain calls based on domain and noticed that the API does not account for it. It's pretty easy to include my own API wrapper and it seems to be the best approach to simply avoid the "api.v1" and rather build my own? Possible, but the core emits an event when the API receives a call that we can listen to. These are the functions we have at our disposal:

// To emit an event:

\Neoan3\Core\Event::dispatch($eventName, $params =[])

// To listen to an event:

\Neoan3\Core\Event::listen($eventName, $callBackFunction)

// Listing emitted events:

\Neoan3\Core\Event::getFiredEvents()

// Listing registered listeners:

\Neoan3\Core\Event::getRegisteredListeners()

Fair enough, but how does that help us with our problem? As mentioned before, the core itself uses these events and we can simply listen to them. In this case, the event Core\\Api::incoming is what I needed to hook into. In the most simple example, I can open up the default.php file and add:

...
\Neoan3\Core\Event::listen('Core\\Api::incoming', function (){
    header('Access-Control-Allow-Origin: https://my-domain.com');
});

And voila, now the callback-function is executed whenever the API receives a call.

Of course, the event-functionality is actually meant to be used for custom events (with less awkward event-names?) in your business logic, but let's look at some useful events the neoan3 core fires:

Prior to routing to a 404

Core\\Route::notFound

This event emits an array as parameter containing "component". Or in other words, the endpoint the link/user intended to visit. While you can easily define your own 404 response in neoan3, this is an easy way to add additional logic (e.g. logging) to a 404 call.

API: Prior to returning a JSON to the client

Core\\Api::beforeAnswer

This event's parameter carries the answer (as array, fired before conversion into JSON) as well as the response code (usually 200 unless the call is invalid) and enables you to execute a additional (or conditional) logic of a higher level (let's say your users have an API quota for instance).

API: Error events

Core\\Api::error

This event holds error-messages you can react to. Modern applications might react with front-end logic, but maybe you want to throttle requests, block IPs etc. server-side.

Before anything?

Core::beforeInit

So this one is bewildering: The core emits this event (again with the requested component) after variables and auto-loading have been established but BEFORE ANYTHING ELSE is executed. I have no idea what you can do with this, but in theory you can execute something like a "middleware for the core".

Posted on by:

sroehrl profile

neoan

@sroehrl

Hello! I am a full stack web dev with roots in LAMP but hack away in Node-environments as well; explore in rust; thrive in open source

Discussion

markdown guide