Event-driven programming with PHP5 and Midgard2

Event-driven programming is a paradigm that is especially familiar for GUI and JavaScript programmers. For example, it is the style used in Node.js. In the traditional PHP space it hasn't been utilized that much, but here is an example how to do it with GObject signals as provided by Midgard2.

Connection events

First of all we prepare a Midgard connection:

// Open Midgard connection
$config = new midgard_config();
$config->read_file_at_path(ini_get('midgard.configuration_file'));

If we want to, we can also get a signal when the connection is open:

// Do stuff when we get a DB connection
midgard_connection::get_instance()->connect('connected', function($connection) {
    echo "Connected, Midgard version " . mgd_version() . "\n";
});

Then we just open the actual connection:

midgard_connection::get_instance()->open_config($config);

Object IO events

In Midgard event stored content object fires signals for each IO event. For example, you can get called when a new object is created (action-created), or when user is attempting to delete an object (action-delete-hook).

Here is a simple listener that gets fired when any article has been updated:

// Whenever an article is updated we want to know about it
midgard_object_class::connect_default('org_midgardproject_news_article', 'action-updated', function($object) {
    echo $object->title . " was updated\n";
});

Query events

Database queries also have signals available. Let us prepare a query:

// Query for articles
$q = new midgard_query_select(
    new midgard_query_storage('org_midgardproject_news_article')
);
$q->add_order(new midgard_query_property('metadata.created'), SORT_DESC);

Before the actual query is executed we can get called. Here we can for example add constraints to filter out information the user is not allowed to see:

// When query starts executing we may add constraints to it
$q->connect('execution-start', function($executor) {
    // Only query articles with a title that starts with "Hello"
    $executor->set_constraint
    (
        new midgard_query_constraint
        (
            new midgard_query_property('title'),
            'LIKE',
            new midgard_query_value('Hello%')
        )
    );
});

Similarly we can get called after the query has executed in order to process the results:

// When query is done, we may do something with the results
$q->connect('execution-end', function($executor) {
    // Increase the score of each queried article by one
    array_walk($executor->list_objects(), function($object) {
        $object->metadata->score++;
        $object->update();
    });
});

Then we just execute the query:

$q->execute();

Wrapping up

On my system running the code above produces the following output:

$ php -c php.ini evented.php 
Connected, Midgard version 10.05.4
Hello world! was updated

Signals can be a very useful way to achieve clean separation of concerns in your application without excessive use of class hierarchies and frameworks. Midgard2 provides these signals not only inside your own code, but also between processes via D-Bus. Developers working in a pure-PHP environment can do something similar via the Zeta Components SignalSlot library.

The full example file is also available.


Read more Midgard posts.