PHP: How to realize event handler? - php

I want to add custom event handler to object's method.
I've got a class with method.
class Post {
public function Add($title) {
// beforeAdd event should be called here
echo 'Post "' . $title . '" added.';
return;
}
}
I want to add an event to method Add and pass method's argument(s) to the event handler.
function AddEventHandler($event, $handler){
// What should this function do?
}
$handler = function($title){
return strtoupper($title);
}
AddEventHandler('beforeAdd', $handler);
Is it possible to do something like this? Hope my question is clear.

Should be pretty easy using the functions defined here http://www.php.net/manual/en/book.funchand.php
In particular you should keep an handler array (or array of arrays if you want multiple handlers for the same event) and then just do something like
function AddEventHandler($event, $handler){
$handlerArray[$event] = $handler;
}
or
function AddEventHandler($event, $handler){
$handlerArray[$event][] = $handler;
}
in case of multiple handlers.
Invoking the handlers then would be just matter of calling "call_user_func" (eventually in a cycle if multiple handlers are needed)

Well, if you are using < php 5.3 then you cannot create a closure in such a way, but you can come close with create_function(); This would be
$handler = create_function('$title', 'return strtoupper($title);');
Then you store $handler in the class and you can call it as you desire.

Methods
You have multiple methods how to do it described by ircmaxell here.
And here is ToroHook used in ToroPHP (Routing lib).
Hook
class ToroHook {
private static $instance;
private $hooks = array();
private function __construct() {}
private function __clone() {}
public static function add($hook_name, $fn){
$instance = self::get_instance();
$instance->hooks[$hook_name][] = $fn;
}
public static function fire($hook_name, $params = null){
$instance = self::get_instance();
if (isset($instance->hooks[$hook_name])) {
foreach ($instance->hooks[$hook_name] as $fn) {
call_user_func_array($fn, array(&$params));
}
}
}
public static function remove($hook_name){
$instance = self::get_instance();
unset($instance->hooks[$hook_name]);
var_dump($instance->hooks);
}
public static function get_instance(){
if (empty(self::$instance)) {
self::$instance = new Hook();
}
return self::$instance;
}
}
Using hook
It is simple call it like this:
ToroHook::add('404', function($errorpage){
render("page/not_found", array("errorpage" => $errorpage));
});

Take look on my sphido/events library:
it's easy to use (few lines of code)
based on PHP Function handling
allow prioritising listeners
add/remove listeners
filter values by functions
stop propagation in function chain
add default handler
Event handler example
on('event', function () {
echo "wow it's works yeah!";
});
fire('event'); // print wow it's works yeah!
Filter function example
add_filter('price', function($price) {
return (int)$price . ' USD';
});
echo filter('price', 100); // print 100 USD

Related

Inline interface implementation - Implement interface methods at declaration

I come from java, where we can do something like this:
Action.java:
public interface Action {
public void performAction();
}
MainClass.java:
public class MainClass {
public static void main(String[] args) { //program entry point
Action action = new Action() {
public void performAction() {
// custom implementation of the performAction method
}
};
action.performAction(); //will execute the implemented method
}
}
As you can see, I'm not creating a class which implements Action, but I'm implementing the interface directly on declaration.
Is something like this even possible with PHP?
What I've tried:
action.php:
<?php
interface Action {
public function performAction();
}
?>
myactions.php:
include "action.php";
$action = new Action() {
public function performAction() {
//do some stuff
}
};
What I get:
Parse error: syntax error, unexpected '{' in myactions.php on line 3
So, my question is: is something like this possible with PHP? How should I do it?
With PHP 7, this has become possible with anonymous classes.
$action = new class implements Action() {
public function performAction() {
//do some stuff
}
};
No, can't. PHP doesn't offer anonymous classes like Java does. You can however try to simulate the behaviour you want, but the results will be...mixed at best.
Here's some code:
interface Action
{
public function performAction();
}
class MyClass
{
public function methodOne($object)
{
$object->performAction(); // can't call directly - fatal error
// work around
$closure = $object->performAction;
$closure();
}
public function methodTwo(Action $object)
{
$object->performAction();
}
}
$action = new stdClass();
$action->performAction = function() {
echo 'Hello';
};
$test = new MyClass();
$test->methodOne($action); // will work
$test->methodTwo($action); // fatal error - parameter fails type hinting
var_dump(method_exists($action, 'performAction')); // false
var_dump(is_callable(array($action, 'performAction'))); // false
Hope it helps!

Assign functions from another file to a Class

I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".

Create and use anonymous object in PHP

Say I have a simple class and I create it and call a function on it like this:
class tst
{
private $s = "";
public function __construct( $s )
{
$this->s = $s;
}
public function show()
{
return $this->s;
}
}
$t = new tst( "hello world" );
echo "showing " . $t->show() . "\n";
Is there any syntax or workaround that will allow me to instantiate an instance of tst and call the show() function without assigning the object to a variable? I want to do something like:
echo new tst( "again" )->show();
I don't want to declare my functions as static as I want to use them in both of the above examples.
You can't do what you want exactly, but there are workarounds without making things static.
You can make a function that returns the new object
function tst( $s ) {
return new tst( $s );
}
echo tst( "again" )->show();
To answer your question:
public static function create( $s )
{
return new tst($s);
}
public function show()
{
return $this->s;
}
The above will allow you to do tst::create("again")->show(). You can rename create as you like.
Agile Toolkit uses this approach everywhere. It uses add() method wrapper which is defined for global object ancestor. Here is some real-life code:
$page
->add('CRUD')
->setModel('User')
->setMasterField('admin',false);
This code creates 'CRUD' view, puts it on the page, creates and links with Model_User class instance which receives additional condition and default value for boolean 'admin' field.
It will display a CRUD control on the page with add/edit/delete allowing to edit all users except admins.
Here is code to describe concept:
class AbstractObject {
public $owner;
function add($class){
$c=new $class;
$c->owner=$this;
return $c;
}
}
class Form extends AbstractObject {
function dosomething(){
return $this;
}
}
class OtherForm extends Form {}
$object->add('Form')->dosomething()->owner
->add('OtherForm'); // etc
I think it's awesome and very practical approach.
p.s. I have to note new syntax for exceptions:
throw $this->exception('Something went bad');
using $this links exception to the object, which is at fault, which also can set default class for exception.

how to pass a parameter to method with php's is_callable

I have to create a variable that is callable with php's is_callable
I have done this:
$callable = array(new MyClass, 'methodName');
But I want to pass a parameter to the method.
How can I do that?
Cause using symfony's event dispatcher component will be like:
$sfEventDispatcher->connect('log.write', array(new IC_Log('logfile.txt'), 'write'));
The first parameter is just a event name, the second is the callable variable.
But I can only call the write method, I want to pass a parameter to it.
Since PHP 5.3 you can use anonymous functions.
You should connect the listener like this:
$dispatcher->connect('my.event', function($event) {
$my_class = new MyClass;
$my_class->myMethod($event, array('my_param' => 'my_value'));
});
Then you will be able to get the parameters array in the listener:
class MyClass {
public function myMethod(sfEvent $event, $parameters) {
$my_value = $parameters['my_param'];
$event_param_value = $event['event_param'];
}
}
Now you can notify the event normally:
$dispatcher->notify(new sfEvent($this, 'my.event', array('event_param' => 'event_param_value')));
Take care that this listener can't be disconnected.
If you need to disconnect it, put the anonymous function in a variable:
$dispatcher->connect('my.event', $my_listener = function($event) {
$my_class = new MyClass;
$my_class->myMethod($event, array('my_param' => 'my_value'));
});
You should be able to disconnect with:
$dispatcher->disconnect('my.event', $my_listener);
You don't pass parameters to your listener callbacks (without extending the core). Symfony will be the one calling it and will pass an event object. If you need additional info, you can create a different method that calls another method where you can control the parameters.
$callable1 = array(new MyWriter, 'write1');
$callable2 = array(new MyWriter, 'write2'); // or $callable2 = array($callable1[0], 'write2');
$sfEventDispatcher->connect('log.write', $callable1);
$sfEventDispatcher->connect('log.write', $callable2);
And your callback class methods can be something like:
class MyWriter
{
public function write($event, $num)
{
// do something
}
public function write1($event)
{
$this->write($event, 1);
}
public function write2($event)
{
$this->write($event, 2);
}
}
Alternatively, you can create properties that act as state that your write function can check:
class MyWriter
{
public $state = 1;
public function write($event)
{
if ($this->state == 1) {
// do this
} else {
// do this instead
}
}
}
This is trickier as you'd have to set state before a pertinent event is triggered which may not prove feasable, depending on the specifics of your situation:
$callable[0]->state = 2;

Object's state in the Template method design pattern

Here is an implementation example of the algorigthm in the base absctract class from http://sourcemaking.com/design_patterns/template_method/php
public final function showBookTitleInfo($book_in) {
$title = $book_in->getTitle();
$author = $book_in->getAuthor();
$processedTitle = $this->processTitle($title);
$processedAuthor = $this->processAuthor($author);
if (NULL == $processedAuthor) {
$processed_info = $processedTitle;
} else {
$processed_info = $processedTitle.' by '.$processedAuthor;
}
return $processed_info;
}
I don't like it becase I think, that "showBookTitleInfo" knows too much about the methods it calls.
Here is another example
abstract class template_method {
var $state;
public function __construct() {
$this->state = 0;
}
public function processEvent( $event ) {
$this->doFirstStep( $event );
$this->doSecondStep( $event );
}
abstract public function doFirstStep( &$event );
abstract public function doSecondStep( &$event );
}
class CustomLogic extends template_method {
public function doFirstStep( &$event ) {
echo __METHOD__.": state: ".$this->state." event: $event\n";
$this->state++;
}
public function doSecondStep( &$event ) {
echo __METHOD__.": state: ".$this->state." event: $event\n";
$this->state++;
}
}
why we pass event as by-reference, if we don't change its value?
How should I implement "my steps" logic, if they are using current state, can modify its value, and other steps can read modified value and can modify it too?
For example, I want to implement cost counting mechanism for scheduled message sending - simple and reccurent(ex: every Mon, Fri until 23.05.2009).
So, I implement the algorithm in abstract class as following:
abstract class AbstractCostCounter {
public function countNotReccurentSendingCost($messageObj) {
$totalMessages = $messageObj->getTotalMessages(); // multiple recipients are allowed
$message_cost = 1; // just to give you an idea
$this->cost = $totalMessages * $message_cost;
}
abstract public function countOptional();
// I pass $messageObject not as by-reference, because it hasn't to be modified
public function countCost( $messageObject ) {
$this->countNotReccurentSendingCost( $messageObject );
$this->countOptional( $messageObject );
}
}
class TemplateNotReccurentCostCounting {
public function countOptional($messageObj) {
// do nothing
}
}
class TemplateReccurentCostCounting {
public function countOptional($messageObj) {
$notReccurentSendingCost = $this->cost;
$totalMessagesInScheduledPlan = $messageObj->getTotalMessagesInScheduledPlan();
$reccurentSendingPlanCost = $notReccurentSendingCost * $totalMessagesInScheduledPlan;
$this->cost = $reccurentSendingPlanCost;
}
}
Am I moving in the right direction?
Is it where Template method design pattern should be implemented?
Please let me know, if it is something wrong with this code.
P.S. cost counter is not a production code. I wrote it because I wanted to give you an idea.
Thanks, in advance
The template method pattern gives the parent class a lot of control, the parent class has to know a lot about the abstract methods (their signature) because it has to 'control' the algorithm. BTW the concrete method in the parent class has to be final.
You have no advantage with your firstStep secondStep methods, I could implement what I want in stepOne and do nothing in stepTwo...
The question is when would you want to use Template Method Pattern, not how to rewrite it to give more flexibility :)

Categories