Buongiorno a tutti.

Con il rilascio di php 5.3 è stato inserito un nuovo elemento di programmazione già presente in diversi linguaggi, estremamente versatile e dinamico: la gestione delle Closure di cui riporto il link ufficiale della documentazione php qui sotto:

http://it2.php.net/manual/en/functions.anonymous.php

quello che quindi in javascript è

codice:
var test=function(aValue){
    return aValue*10;
    }

alert( test(10) ) // stampa 1000
è ora possibile farlo in php

Codice PHP:
$test=function($val){
    echo 
$val*10;
    }

$test(10// stampa 100 
ed è proprio su questo principio che ho sviluppato un gestore di eventi molto semplice ma efficace.

Già in un post di qualche mese fà mi ero chiesto come poter implementare un sistema di eventi in php e sinceramente, anche se avevo ricevuto validi workaround e consigli, mi sono scoraggiato in quanto sicuramente mi mancava uno strumento come appunto le Closures.

ora che è a disposizione ho rispolverato il progetto ed ecco la classe con tutti i commenti del caso

Spero possa servire a qualcuno!!

Codice PHP:
<?php
// +------------------------------------------------------------------------+
// | events.class.php                                                 |
// +------------------------------------------------------------------------+
// | Copyright (c) Temperini Mirko 200x. All rights reserved.                  |
// | Version       0.1                                                     |
// | Last modified 21/08/2009                                               |
// | Email         [email]dottwatson@hotmail.it[/email]                                   |
// | Web           [url]http://www.phpmonster.org[/url]                                |
// +------------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or modify   |
// | it under the terms of the GNU General Public License version 2 as      |
// | published by the Free Software Foundation.                             |
// |                                                                        |
// | This program is distributed in the hope that it will be useful,        |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of         |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          |
// | GNU General Public License for more details.                           |
// |                                                                        |
// | You should have received a copy of the GNU General Public License      |
// | along with this program; if not, write to the                          |
// |   Free Software Foundation, Inc., 59 Temple Place, Suite 330,          |
// |   Boston, MA 02111-1307 USA                                            |
// |                                                                        |
// | Please give credit on sites that use class.upload and submit changes   |
// | of the script so other people can use them as well.                    |
// | This script is free to use, don't abuse.                               |
// +------------------------------------------------------------------------+
class events{

    
/*
    *inizializzo la classe eventi affinchè vada a 
    *conoscere tutti i metodi presenti nel suo utilizzatore
    *e ne raccolgo i nomi per inizializzare i nuovi eventi
    *
    ********************************************************/
    
public function __construct($related_class){
        
//  i metodi  magici che non dovranno essere prese in considerazione
        //  dal processo automatico  
        
$denied=array('__construct','__destruct');
        
// la lista dei metodi dell' utilizzatore
        
$methods=get_class_methods($related_class);

        
        foreach(
$methods as $method){
            
// se non è nella lista nera
            
if(!in_array($method,$denied)){
                
// supponendo il methodo foo il nome evento sarà onFoo
                
$onMethod=$this->set_event_name($method);
                
//creo il nuovo evento, ovviamente una funzione vuota
                
$this->$onMethod=function(){};
                }
            }        
        } 

    
/*
    *access:    public
    *params:    func_name->il nome della funzione a cui sarà agganciato l' evento
    *           $function: la Closure che sarà eseguita         
    */
    
public function addEvent($func_name,$function){
        
// supponendo $func_name = foo il nome evento sarà onFoo
        
$onMethod=$this->set_event_name($func_name);
        
//associo ad uan variabile della classe elements la Closure appena dichiarata
        
$this->$onMethod=$function;
        return 
true;
        }

    
/*
    *access:    public
    *params:    func_name->il nome della funzione da cui sarà rimosso l' evento         
    */
    
public function removeEvent($func_name){
        
// supponendo $func_name = foo il nome evento sarà onFoo
        
$onMethod=$this->set_event_name($func_name);
        if(isset(
$this->$onMethod)){
            
//se esiste, associo all' evento una Closure vuota, come in origine
            
$this->$onMethod=function(){};
            return 
true;
            }
        return 
false;
        }


    
/*
    *access:    public
    *params:    func_name->il nome della funzione a cui sarà agganciato l' evento
    *           argument 1,argument 2,argument 3 ecc...         
    */

    
public function fireEvent(){
        
//gli argomenti passati alla funzione
        
$__args=func_get_args();
        
        
//il nome del metodo di riferimento
        
$methodName=$__args[0];
        
        
//lo elimino perchè i successivi sareanno passati alla Closure
        
unset($__args[0]);
        
        
// supponendo $onMethod = foo il nome evento sarà onFoo
        
$onMethod=$this->set_event_name($methodName);
        
        
        if(isset(
$this->$onMethod)){
            
            
//passaggio fondamentale per utilizzare la Closure!! associo la Closure ad una variabile 
            
$event=$this->$onMethod;
            
            
//ci sono argomenti per la mia Closure?
            
if(!empty($__args)){
                
$res=array();
                foreach(
$__args as $a_key=>$a_val){
                    
// un nomedi variabile per passaro alla Closure
                    
$varName="to_function_".$a_key;
                    
                    $
$varName=$a_val;
                    
                    
$res[]='$'.$varName;
                    }
                
//lancio l' evento, con sintassi $vent($to_function_2,$to_function_3,$to_function_4...)
                
eval('$event('.implode(',',$res).');');
                }
            else{
                
// nonc'erano argomenti, quindi la lancio così com'è!
                
$event();    
                }
            return 
true;
            }
        return 
false;
        }
    
    
    private function 
set_event_name($name=""){
        
$name=strtolower($name);
        return 
'on'.ucfirst($name);
        }
    
    }


class 
test{
    
    public function 
__construct(){
        
//  aggancio alla classe il gestore eventi,il quale accetta come argomento la classe stessa 
        
$this->events=new events($this);    
        }    
    
    public function 
launch(){
        echo 
"i'm the method ".__METHOD__." , ready to go!
"
;
        
        
// NECESSARIO per lanciare l' evento alla fine della sua esecuzione;
        // ovviamente... PRIMA di un eventuale return!
        
$this->events->fireEvent(__FUNCTION__);
        }

    public function 
arrived($now){
        if(
$now 1000000)
            echo 
"
the rocket isn't arrived on the moon :(
"
;
        else
            echo 
"
wow! is on the moon!! 
"
;
        }
    }



/***************************************
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
*   TEST TEST TEST TEST TEST TEST TEST
****************************************/


// inizializzo la classe di test, in cui nel metodo costruttore 'aggancio' la classe che gestisce gli eventi
$test=new test;


// un semplice test senza eventi
$test->launch();
echo 
"
"
;


// creo un evento che assumerà il nome 'onLaunch' nella classe events (quindi $test->events->onLaunch) 
// 2 paramtri: il metodo target, e la Closure da eseguire
$test->events->addEvent('launch',
    function(
$when=0){
        
$when=($when 0)?$when:time();
        echo 
"
the rocket was launched on "
.date('Y-m-d',$when)." at ".date('H:i:s',$when)." !!
"
;
        }); 

$test->launch();
echo 
"
"
;


// lancio l'evento in maniera indipendente, senza richiamare launch
$test->events->fireEvent('launch',time()-86400);
echo 
"
"
;

//rimuovo l'evento
$test->events->removeEvent('launch');
$test->launch();
echo 
"
"
;


/*
* ora una prova su una variabile ESTERNA a tutto, definita chissà dove e chissà come e la modifico
*/
$VAR=1000000;
echo 
"
the rocket is about 
{$VAR} kilometers far from the moon!
"
;

$test->events->addEvent('launch',
    function(
$kilometers=0) use (&$VAR){
        
$VAR-=$kilometers;
        echo 
"
the rocket is about 
{$VAR} kilometers far from the moon!!
"
;
        
$VAR-=rand(100,10000);
        }); 

$test->events->fireEvent('launch',100);


$test->events->fireEvent('launch',200);

echo 
"
the rocket is about 
{$VAR} kilometers far from the moon!
"
;




$test->events->addEvent('launch',
    function(
$kilometers=0) use (&$VAR,&$test){
        
$VAR-=$kilometers;
        echo 
"
the rocket is about 
{$VAR} kilometers far from the moon!!
"
;
        
$VAR-=rand(100,10000);
        
$test->arrived($VAR);
        }); 

$test->events->fireEvent('launch',300);
?>