jgswift/observr

PHP 5.5+ event layer

0.2.5 2014-12-10 00:54 UTC

This package is not auto-updated.

Last update: 2024-04-13 13:56:18 UTC


README

PHP 5.5+ event layer

Build Status Scrutinizer Code Quality Latest Stable Version License Coverage Status

Description

observr is a generic event layer that provides a flexible foundation for event handling in a domain-agnostic and non-intrusive way.

Installation

Install via cli using composer:

php composer.phar require jgswift/observr:0.2.*

Install via composer.json using composer:

{
    "require": {
        "jgswift/observr": "0.2.*"
    }
}

Dependency

Usage

Subject

The Subject layer is a flexible interface/trait combination that provides a generic observer pattern implementation.

Basic example

class User implements observr\SubjectInterface
{
    use observr\Subject;
}

$user = new User;
$user->attach("login",function($sender,$e) {
    return "login successful";
});

var_dump($user->setState("login")); // returns "login successful"

State Change

observr\Event delivers a combination EventInterface and EventAwareInterface to the observing callbacks and can be used to define pass-thru state variables.

$user = new User;
$user->attach("login",function($sender,$e) {
    $e->cancel();  // manual cancellation
});

$event = new observr\Event($user);
$user->setState("login",$event)

var_dump($event->isCanceled()); // returns true

To implement a custom event interface just inherit observr\Event or implement EventInterface and EventAwareInterface

EventInterface (abbr.)

State status and exception container

interface EventInterface {
    public function isComplete();

    public function isSuccess();

    public function isFailure();

    public function isCanceled();

    public function getException();

    public function setException(\Exception $exception);
}

EventAwareInterface (abbr.)

Performs state changes for EventInterface

interface EventAwareInterface {
    public function cancel(EventInterface $event = null);

    public function complete(EventInterface $event);

    public function fail(EventInterface $event);

    public function succeed(EventInterface $event);
}

Event Namespacing

In order to handle events differently depending on package behavior it is possible to attach/detach with namespaces.

$user = new User;
            
$user->attach('login',function($sender,$e) {
    echo 'default login';
});

$user->attach('login.myNamespace',function($sender,$e) {
    echo 'my custom login';
});

$user->setState('login'); // echo 'default login' & 'my custom login'

$user->detach('login.myNamespace'); // removes only the namespaced handler

$user->setState('login'); // echo 'default login'

Success, Failure, Complete, Cancel

observr\Event also implements the observer pattern itself and can be used to validate event results. The validation constants are COMPLETE, FAILURE, SUCCESS, and CANCEL

  • COMPLETE is notified when all observers fire without failure
  • FAILURE is notified when any observer throws an exception
  • SUCCESS is notified every time the state is changed without failure or cancellation
  • CANCEL is notifier when any observer invokes cancellation
$user = new User;
$event = new observr\Event($user);

$user->attach("login",function($sender,$e) {
    // success with no cancellation or errors, default behavior
});

$event->attach(observr\Event::COMPLETE,function() {
    echo 'COMPLETE';
});

$event->attach(observr\Event::SUCCESS,function() {
    echo 'SUCCESS';
});

$user->setState("login",$event); // everything worked!
// invokes ...
// COMPLETE
// SUCCESS

$user->attach("login",function($sender,$e) {
    $e->cancel(); // cancels the event
});

$event->attach(observr\Event::CANCEL,function() {
    echo 'CANCELED';
});

$user->setState("login",$event); // manual cancellation
// invokes ...
// COMPLETE
// CANCEL

$user->attach("login",function($sender,$e) {
    throw new \Exception; // causes fault
});

$event->attach(observr\Event::FAILURE,function() {
    echo 'FAILURE'
});

$user->setState("login",$event); // throws exception
// invokes ...
// FAILURE

Emitter

Emitter is a Subject where events are exposed into individual objects

Basic Emitter

class Button implements observr\SubjectInterface {
    use observr\Subject;
}

$button = new Button;

$click = new observr\Emitter('click');

$button->setState($click, function($sender,$e) {
    echo 'CLICKED!';
});

$click($button); // prints 'CLICKED'!

EmitterInterface (abbr.)

interface EmitterInterface extends SubjectInterface {
    public function getName();

    public function emit($e = null);

    public function on(callable $callable);

    public function bind(callable $callable);

    public function unbind(callable $callable);

    public function map(callable $callable);

    public function filter(callable $callable);

    public function __toString();
}

Combining Emitters

class Button {
    function __construct() {
        $this->click = new observr\Emitter('click');
        $this->mouseup = new observr\Emitter('mouseup');
        $this->mousedown = new observr\Emitter('mousedown');
    }
}

$button = new Button;

$combinedClick = $button->click->map(function($sender, $e) {
    /* extra mapping */
})->merge($button->mousedown->map(function($sender,$e) {
    /* extra mapping */
}))->merge($button->mouseup->map(function($sender,$e) {
    /* extra mapping */
}));

$combinedClick($button); // performs click, mousedown & mouseup all together

Filtering

Filtering allows mapping procedures to be applied selectively

$sending = $button->click
  ->filter(function($button,$e) {
    if($button instanceof Button) { // only changes Button to "Sending..."
        return true;
    }

    return false;
})->map(function($button,$e) {
    $button->value = 'Sending...';
});

$sending($button); // triggers click and changes button text to "Sending..."

Streaming

A stream provides an easy way to wrap around multiple subjects at once and listen to many events.

Basic

$bob = new User
$john = new User;

$stream = new observr\Stream('login');

// instruct stream to watch our user login events
$stream->watch($bob);
$stream->watch($john);

$c = 0;
$stream->attach(function($sender,$e=null)use(&$c) {
    $c++; // called twice, this is where we intercept the event
});

// open stream
$stream->open();

// trigger some fake logins
$bob->setState('login');
$john->setState('login');

// close stream
$stream->close();

var_dump($c); // 2

StreamInterface (abbr.)

interface StreamInterface {
    public function close();

    public function getSubjects();

    public function isOpen();

    public function open();

    public function watch($pointer);

    public function unwatch($pointer);

    public function isWatching($pointer);
}

Related Package(s)