PHP property access event system

Installs: 2

Dependents: 0

Stars: 72

Watchers: 7

Forks: 2

Language: PHP

0.1.0 2014-03-04 17:17 UTC

README

PHP variable event system

Quality / Metrics Releases Downloads Licence Build Status Coverage Status Latest Stable Version Latest Unstable Version Total Downloads License

Installation

Install via composer:

php composer.phar require leedavis81/vent:dev-master

Usage

Have you ever needed to hook an event anytime a PHP variable is read? Maybe you want to ensure complete immutability even within the scope (private) of your class. PHP variable events can be easily created by hooking into the read or write of any variable.

<?php
class Foo
{
    use Vent\VentTrait;

    private $bar;

    public function __construct()
    {
        $this->registerEvent('read', 'bar', function(){
            throw new \Exception('Don\'t touch my bar!');
        });
    }

    public function touchBar()
    {
        $this->bar;
    }
}

$foo = new Foo();
$foo->touchBar(); // Fatal error: Uncaught exception 'Exception' with message 'Don't touch my bar!'

Or you can register a write event to protect the variable from being overwritten (even from within the scope of your class)

$this->registerEvent('write', 'bar', function(){
  throw new \Exception('Don\'t write to my bar!');
});

public function writeToBar()
{
  $this->bar = 'somethingElse';
}        

$foo = new Foo();
$foo->writeToBar(); // Fatal error: Uncaught exception 'Exception' with message 'Don't write to my bar!'        

You can masquerade any value by returning something from your registered event. Note that if multiple events are registered, execution they will stop once one of them returns a response. They are triggered in the order they're registered (first in, first out).

public $bar = 'Bill';

public function __construct()
{
  $this->registerEvent('read', 'bar', function(){
    return 'Ben';
  });
}

$foo = new Foo();
echo $foo->bar; // "Ben"

To pass in parameters into your method simply provide them when registering the event.

Please note there are two reserved strings that if passed in as a parameter will be replaced

  • _OVL_NAME_ replaced with the name of the variable you're accessing (set or get)
  • _OVL_VALUE_ replaced with the value your updating a variable with (set only)
  • _CUR_VALUE_ the current value of the variable
public $bar = 'chips';
public function __construct()
{
  $this->registerEvent('write', 'bar', function($var1, $name, $value, $cur_value){
    echo 'Why ' . $var1 . ' you\'re trying to overwrite "' . $name . '", which contained "' . $cur_value . '" to contain "' . $value . '"';
  }, ['bill', '_OVL_NAME_', '_OVL_VALUE_', '_CUR_VALUE_']);
}

$foo = new Foo();
$foo->bar = 'cheese';      // Why bill you're trying to overwrite "bar", which contained "chips" to contain "cheese"

If you are returning a response on your event, this can be retained to prevent additional execution on further reads. If you don't return anything from your action (or return a null) then nothing is retained and additional reads will cause re-execution of your registered action.

public $bar = 'Bill';

public function __construct()
{
  $this->registerEvent('read','bar', function(){
    sleep(1);
    return microtime();
  }, null, true);     // pass in "true" here (defaults to false)
}


$foo = new Foo();
var_dump($foo->bar === $foo->bar);   // true

All events must be registered within the context of your class when using VentTrait. However, if you'd like to register them from a public or protected scope then simply change the scope of the registerEvent method when importing the trait

<?php
class Foo
{
    use Vent\VentTrait {registerEvent as public;}   // allow public event registration

    public $bar;
}

$foo = new Foo();

$foo->registerEvent('read', 'bar', function(){
    throw new \Exception('Don\'t touch my bar!');
});

$foo->bar;  // Fatal error: Uncaught exception 'Exception' with message 'Don't touch my bar!'

Supported Events

Event Name Alias Triggered read get Whenever an attempt to read the variable is performed write set Whenever an attempt to write to the variable is performed delete unset Whenever unset is called on the variable. Setting to null will not work.

But you stole my magic

It's true that this little trait applies a __get and __set method to your class. If your class already has a little magic and these methods have already been applied then they'll overwrite the trait implementation. Maybe not what you want. To get around this you can simply import them with a different method name, and call them in your own magic methods. For example:

class Foo()
{
    use Vent\VentTrait {__get as get;}

    public function __get($name)
    {
        // You're own magic stuff will go here.
        return $this->get($name);   // Fire off the vent magic (if you need it)
    }
}

todos

  • Allow event triggering for array offset reads $foo->bar['offset'];
  • Add a method to remove variables from proxy (to increase perf)