26 marzo, 2009

js.php, una clase PHP cual objeto JavaScript

Andrea Giammarchi vuelve a demostrar un profundo conocimiento de los lenguajes de programación, necesario para llegar a proponer una forma de trabajar con objetos en PHP que copia el sistema de prototipos de JavaScript y permite realizar cosas como las siguientes:

// podemos asignar prototipos en cualquier sitio ...
JSObject::$prototype->getName = function($self){
    return  $self->name;
};

$o = JSObject();

// ... incluso después de la creación de instancias de JSObject 
JSObject::$prototype->writeAge = function($self){
    echo $self->getName().' tiene '.$self->age.' años.';
};

// asignar propiedades
$o->name    = 'Andrea';
$o->age     = 30;

// probar algún método JS
echo    $o->toJSONString();
// {"name":"Andrea","age":30}

echo    $o;
// [object JSObject]

echo    $o->toSource();
// O:8:"JSObject":2:{s:4:"name";s:6:"Andrea";s:3:"age";i:30;}

// o probar métodos en tiempo de ejecución
$o->writeAge();
// Andrea tiene 30 años

// podemos asignar un método personalizado en tiempo de ejecución
$o->getSurname = function($self){
    return  'Giammarchi';
};

// los métodos personalizados tienen prioridad sobre el prototipo
$o->getName = function($self){
    return  $self->name.' '.$self->getSurname();
};

$o->writeAge();
// Andrea Giammarchi tiene 30 años.


// no es hermoso? y aún falta más para la compatibilidad hacia atrás (backward):

// prototipado via resolución de nombres de función
JSObject::$prototype->getName = 'JSObject_getName';

// la función con un prefijo evita conflictos
function JSObject_getName($self){
    return  $self->name;
}

// un simple caso de prueba
$o = JSObject();
$o->name    = 'Andrea';

echo    $o->getName();
// Andrea

// Y obviamente podemos hacer lo mismo con create_function:

// prototype via lambda
JSObject::$prototype->getName = create_function('$self','
    return  $self->name;
');

La implementación utiliza la extensión SPL disponible en el núcleo de PHP desde la versión 5.1:

/** js.php basic JSObject implementation
 * @author  Andrea Giammarchi
 * @blog    WebReflection.blogspot.com
 * @license Mit Style License
 */
class JSObject implements ArrayAccess {

    // public static prototype
    static  public  $prototype;

    // ArrayAccess Interface
    public  function    offsetExists($index){return  isSet($this->$index);}
    public  function    offsetGet($index){return  $this->$index;}
    public  function    offsetSet($index, $value){$this->$index = $value;}
    public  function    offsetUnset($index){unset($this->$index);}

    // Invoked lambdas via $this or self::$prototype
    public  function    __call($index, array $arguments){

        // inject $this as first argument
        array_unshift($arguments, $this);

        // invoke the callback
        return  call_user_func_array(
        
            // if it has been assigned to the object itself
            isset($this->$index) ?

                // it has priority
                $this->$index :
                
                // otherwise invoke from self::$prototype
                self::$prototype->$index
            ,
            $arguments
        );
    }

    // JS like behavior, ready for extends
    public  function    __toString(){
        return  '[object '.get_class($this).']';
        // e.g. [object JSObject]
    }

    // Relevant Global Object implementations
    public  function    getPrototypeOf(){
        return  get_class($this);
    }
    public  function    hasOwnProperty($index){
        return  isset($this->$index);
    }
    public  function    isPrototypeOf(/* string */ $class){
        return  $this instanceof $class;
    }
    public  function    toSource(){
        // we could implement __sleep and __wakeup
        // to avoid closure serialization (not possible yet)
        return  serialize($this);
    }
    public  function    toString(){
        return  $this->__toString();
    }


    // not standard method, anyway useful
    public  function    toJSONString(){
        // we could parse properties in a foreach
        // to filter meaningless closures, if any
        return  json_encode($this);
    }
}

// the global prototype is a JSObject too
JSObject::$prototype = new JSObject;

// JSObject factory: e.g. $myobj = JSObject(); // rather than new JSObject;
function JSObject(){
    return  new JSObject($object);
}

Como apunta Andrea, este puede ser el punto de partida para la creación de frameworks de PHP utilizando este estilo de programación. También menciona que unas extensiones estables de PECL que permitiesen una sobrecarga de operadores (de la que yo no soy muy amigo, por cierto) le permitiría crear una versión de PHP estilo JavaScript. Y aprovechar eso para conseguir que PHP se comporte como JavaScript en el lado del servidor al estilo Jaxer, pero utilizando PHP que está mucho más difundido.

WebReflection: [js.php] A JavaScript Object Like PHP Class.

Publicar un comentario en la entrada

Últimos links en indiza.com