Documente Academic
Documente Profesional
Documente Cultură
Los mtodos mgicos mgicos los provee PHP y nos permiten realizar
ciertas tareas orientadas a objetos.
Los mtodos mgicos identifican por el uso de dos guiones bajos __ como
prefijo, y funcionan como interceptores que se llaman automticamente
cuando ocurre una condicin.
__clone()
En PHP 4 se poda copiar un objeto simplemente asignndolo de una variable a otra. Y de esta
forma se poda modificar la copia sin que ello afectara al original.
$objecto1->nombre = "nombre 1";
$objecto2 = $objecto1;
$objecto2->nombre = "nombre 2";
echo $objecto1->nombre; // nombre 1"
Pero en PHP 5 los objetos son asignados por referencia. Lo que significa que
si en dos variables asignamos el mismo objeto lo que obtendremos ser dos
referencias al mismo objeto. Por lo que necesitamos algo ms si queremos
realizar una copia exacta pero no enlazada de un objeto. Y para ello existe la
palabra reservada clone.
$objecto1->nombre = "nombre 1";
$objecto2 = clone $objecto1;
$objecto2->nombre = "nombre 2";
echo $objecto1->nombre; // nombre 1"
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getNombre() {
return $this->nombre;
}
public function setNombre($nombre) {
$this->nombre = $nombre;
}
public function getEmail() {
return $this->email;
}
public function setEmail($email) {
$this->email = $email;
}
}
$obj = new Objeto(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
echo $p->getId(); //2
Hay que tener en cuenta que estos mtodo solo se llamarn desde fuera del
objeto. Dentro utilizaremos la referencia al objeto $this.
__toString
Este mtodo es quizs uno de los mtodos mgicos menos importantes.
Permite asignar una cadena (string) al objeto que ser mostrada si el objeto
es usado como una cadena. Osea el valor que mostrar si se intenta hacer
echo $objeto.
class Objeto {
private $id;
private $nombre;
private $email;
//...
public function __toString(){
return "esto es una prueba";
}
}
$obj = new Objeto(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
echo $p; //esto es una prueba
un error.
En PHP 5.2 y versiones superiores, el mtodo __toString ser invocado
automticamente cuando se utilice las funciones de
impresin echo(), print() y printf(). Esta ltima nicamente cuando se utiliza
el modificador %s.
_call
Este mtodo mgico es llamado automticamente cuando se intenta llamar
a un mtodo que no esta definido en la clase o es inaccesible dentro del
objeto (por ejemplo un mtodo privado).
Este mtodo recibe dos parmetros, uno es el nombre del mtodo al que se
intenta invocar, y el otro los parmetros que le hemos pasado al mtodo al
que hemos intentado llamar.
class Objeto {
private $id;
private $nombre;
private $email;
//...
public function __toString(){
return "esto es una prueba";
}
public function __call($metodo, $args) {
$args = implode(', ', $args);
echo "fallo al llamar al mtodo $metodo() con los argumentos
$args";
}
}
$obj = new Objeto(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
echo $p;
$p->noExiste();
Visto este ejemplo nos puede parecer que este mtodo solo sirve para
mostrar un mensaje evitando el error que se debera mostrar por intentar
acceder a algo que no existe. Pero tambin se puede utilizar para crear
'setter' y 'getters' dinmicos sin tener que utilizar los mtodos mgicos
__set() y __get(). Ya que es posible que la utilizacin de estos ltimos no nos
guste (los atributos se acceden como si fueran pblicos para que se
invoquen los mtodos __get() y __set()).
Vamos a ver un ejemplo.
class Objeto {
private $id;
private $nombre;
private $email;
function __construct($id, $nombre, $email) {
$this->id = $id;
$this->nombre = $nombre;
$this->email = $email;
}
function __clone() {
$this->id = ++$this->id;
}
public function __toString() {
return "esto es una prueba";
}
public function __call($metodo, $params = null) {
// todo en minsculas para evitar problemas. Por ejemplo
setNombre
$metodo = strtolower($metodo);
$prefijo = substr($metodo, 0, 3);
$atributo = substr($metodo, 3);
if ($prefijo == 'set' && count($params) == 1) {
if (property_exists(__CLASS__, $atributo)) {
$valor = $params[0];
$this->$atributo = $valor;
} else {
echo "No existe el atributo $atributo.";
}
} elseif ($prefijo == 'get') {
if (property_exists(__CLASS__, $atributo)) {
return $this->$atributo;
}
return NULL;
} else {
echo 'Mtodo no definido <br/>';
}
}
}
$obj = new Objeto(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
$p->noExiste(); //Mtodo no definido
$p->setId(2);
echo $p->getId() . "<br/>"; //2
class Objeto {
//...
public function __call($metodo, $params = null) {
// todo en minsculas para evitar problemas. P.E. setNombre
$metodo = strtolower($metodo);
$prefijo = substr($metodo, 0, 3);
$atributo = substr($metodo, 3);
$nombreClase = get_class($this);
if ($prefijo == 'set' && count($params) == 1) {
if (property_exists($nombreClase, $atributo)) {
$valor = $params[0];
$this->$atributo = $valor;
} else {
echo "No existe el atributo $atributo.";
}
} elseif ($prefijo == 'get') {
if (property_exists($nombreClase, $atributo)) {
return $this->$atributo;
}
return NULL;
} else {
echo 'Mtodo no definido <br/>';
}
}
}
class ClaseHijo extends Objeto{
protected $apellido = "apellido";
}
$obj = new ClaseHijo(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
$p->noExiste();//Mtodo no definido
$p->setId(2);
echo $p->getId() . "<br/>";//2
echo $p->getApellido();//apellido
__callStatic
El mtodo anterior __call era lanzado al intentar llamar a un mtodo inaccesible en el contexto
del objeto. Pero no est pensado si el mtodo es inaccesible en un contexto esttico. Osea que
class Objeto {
protected $id;
protected $nombre;
protected $email;
function __construct($id, $nombre, $email) {
$this->id = $id;
$this->nombre = $nombre;
$this->email = $email;
}
function __clone() {
$this->id = ++$this->id;
}
function who() {
return __CLASS__;
}
public function __toString() {
return "esto es una prueba";
}
public static function __callStatic($metodo, $args) {
echo "Mtodo $metodo ha sido llamado!!!<br/>";
echo "Con los siguientes argumentos: " . implode(", ", $args) .
"<br/>";
}
public function __call($metodo, $params = null) {
// todo en minsculas para evitar problemas. P.E. setNombre
$metodo = strtolower($metodo);
$prefijo = substr($metodo, 0, 3);
$atributo = substr($metodo, 3);
$nombreClase = get_class($this);
if ($prefijo == 'set' && count($params) == 1) {
if (property_exists($nombreClase, $atributo)) {
$valor = $params[0];
$this->$atributo = $valor;
} else {
echo "No existe el atributo $atributo.";
}
} elseif ($prefijo == 'get') {
if (property_exists($nombreClase, $atributo)) {
return $this->$atributo;
}
return NULL;
} else {
echo 'Mtodo no definido <br/>';
}
}
}
class ClaseHijo extends Objeto {
protected $apellido = "apellido";
}
$obj = new ClaseHijo(1, "objeto1", "prueba1@ejemplo.com");
$p = clone $obj;
$p->noExiste();
$p->setId(2);
echo $p->getId() . "<br/>"; //2
echo $p->getApellido(); //apellido
Objeto::algunMetodo('con', 'varios', 'argumentos');
__isset y __unset
Si estn definidos estos mtodo mgicos son llamados automticamente cuando se utilizan las
funciones isset() y unset() sobre un atributo no definido o inaccesible en el objeto. Recordemos
que isset() sirve para comprobar la existencia de una variable y unset() sirve para destruir una
variable. Pero para que la comprobacin se realice sobre atributos de un objeto tendremos que
definir los mtodo mgicos.
El mtodo mgico __isset() recibe un argumento nombre del atributo a comprobar si existe o
no.
El mtodo mgico __unset() recibe un argumento nombre del atributo que queremos destruir.
class Objeto {
protected $id;
protected $nombre;
protected $email;
function __construct($id, $nombre, $email) {
$this->id = $id;
$this->nombre = $nombre;
$this->email = $email;
}
function __isset($atributo) {
return isset($this->$atributo);
}
function __unset($atributo) {
if(isset($this->$atributo))
unset($this->$atributo);
}
}
$obj2 = new Objeto(1, "objeto1", "prueba1@ejemplo.com");
echo isset($obj2->nombre);
__sleep y __wakeUp
Los objetos pueden ser representados en cadenas para poder ser
almacenados. Esto se hace mediante la funcin serialize(). Sin embargo la
cadena resultante en el proceso de serializacin muestra todos los atributos
de objeto, independientemente de que sean publico o privados. Y esto no es
conveniente sobre todo si queremos compartir dicha serializacin.
PHP 5 introdujo dos mtodos mgicos que se invocarn automticamente
durante el proceso de serializacin y de esta forma podemos, por ejemplo,
elegir los atributos que queremos que se muestren en la representacin del
objeto (serializacin) . Para ello, cuando las funciones serialize() y
unserialize() sean llamadas, PHP buscar si los mtodos mgicos __sleep y
__wakeUp(), respectivamente, estn definidos.
En el siguiente ejemplo se va a ver como mediante __sleep() se elije lo que
queremos que se serialize.
class Usuario {
public $nombreUsuario;
public $email;
private $contrasenya;
private $dinero;
public function Usuario($nombre, $pwd, $email,$dinero) {
$this->nombreUsuario = $nombre;
$this->contrasenya = $pwd;
$this->email = $email;
$this->dinero = $dinero;
}
public function ingresar($saldo) {
$this->dinero += $saldo;
}
public function __sleep() {
return array("nombreUsuario", "email");
}
}
$usuario = new Usuario("Pedro", "1234", "prueba@ejemplo.com",100);
$usuario->ingresar(50);
echo "Estado del usuario: <br/>";
print_r($usuario);
echo "<br/>";
$usuarioSerialize = serialize($usuario);
echo "El usuario serializado: <br/>";
echo $usuarioSerialize;
echo "<br/>";
echo "Usuario deseralizado: <br/>";
$obj = unserialize($usuarioSerialize);
print_r($obj);
__invoke
En PHP 5.3 aparece el mtod mgico __invoke que es llamado
automticamente por PHP cuando se intenta llamar a un objeto como si se
tratase de una funcin. Por lo tanto sirve para controlar el comportamiento
de nuestro objeto en dicho caso. Si no definimos el mtodo __invoke y
usamos un mtodo como si se tratase de una funcin obtendremos un error.
class Animal {
public function __invoke() {
echo "Soy un animal";
}
}
$animal = new Animal();
$animal();
$toMultiply = 5;
$multiplier = function($value) use ($toMultiply) {
return $value * $toMultiply;
};
var_dump($multiplier(3));
class MultiplierCallback
{
private $_toMultiply;
public function __construct($numberToMultiply)
{
$this->_toMultiply = $numberToMultiply;
}
public function __invoke($value)
{
return $value * $this->_toMultiply;
}
};
$multiplierObj = new MultiplierCallback(5);
var_dump($multiplierObj(3)) ;
__set_state
Este mtodo mgico nacido en PHP 5.1 es llamado automticamente
cuando una instancia de una clase es pasada a la funcin var_export. Esta
funcin devuelve informacin de la variable pasada como argumento. su
uso es similar a print_r o var_dump. Pero con la diferencia de que la salida
devar_export es cdigo vlido y por lo tanto evaluable por PHP.
Vamos a ver un ejemplo de var_export()
class Prueba{
public $nombre;
public $edad;
function __construct($nombre, $edad){
$this->edad = $edad;
$this->nombre = $nombre;
}
}
$p = new Prueba('Jose',30);
var_export($p);//Prueba::__set_state(array( 'nombre' => 'Jose',
'edad' => 30, ))
var_dump($p2);
Nota: con new self() estamos creando una instancia de la clase Prueba.
Y la salida es la siguiente:
Prueba::__set_state(array( 'nombre'
objeto p2 creado con eval:
object(Prueba)#5 (2) { ["nombre"]=>
int(30) }
objeto p:
object(Prueba)#4 (2) { ["nombre"]=>
int(30) }
objeto p2 modificado:
object(Prueba)#5 (2) { ["nombre"]=>
["edad"]=> int(30) }
class Auto
{
private $ruedas;
private $propietario;
public function __construct()
{
$this->propietario = "Sin dueo";
$this->ruedas = 4;
}
public function getRuedas()
{
return $this->ruedas;
}
public function getPropietario()
{
return $this->propietario;
}
public function setPropietario($nombre)
{
if (is_string($nombre)) {
$this->propietario = $nombre;
}
}
$miAuto = new Auto;
echo "Propietario: " . $miAuto->getPropietario() . "<br />";
De esta forma, podemos acceder a la propiedad sin indicar un propietario
para ella, estamos seguros que va a poseer un valor que tenga sentido