Objetos | métodos mágicos en PHP
En PHP hay un grupo de métodos llamados mágicos que nos pueden ayudar cuando ocurre un evento sobre el objeto, estos métodos son llamados sin la necesidad de ser llamados por usted, por eso su nombre de métodos mágicos.
Lo más probable es que usted conozca 2 de estos si ha utilizado algún otro lenguaje, y estos serian el constructor y el destructor del objeto.
Métodos mágicos
constructor destructor call y callstatic getvalue setvalue isset unset | serialize, sleep unserialize, wakeap tostring invoke setstate clone debuginfo |
En esta ocasión para indicar la visibilidad o niveles de acceso utilizaremos la siguiente nomenclatura:
public
protected
private
Constructor
Probablemente, el método __construct()
es el más conocido de toda la lista y es el método que primero aprende cuando ve programación orientada a objetos en PHP.
<?php class Perro{ public $nome; public $raza; public __construct(string $n,string $r){ $this->nombre = $n; $this->raza = $r; } } $p = new Perro("Yoshi","Terrier"); echo $p->nombre;//deberia darnos Yoshi
Es un ejemplo bastante simple pero funcional
[ADS_A1/]Destructor
El método __destruct()
, se llama cuando el objeto se destruya. No soy muy bueno dando ejemplos y los que encuentre online no eran lo suficiente buenos. Así que imagine que se trata de un juego online de navegador y que tiene un objeto por ejemplo una espada.
class espada{ __destruct(){ echo "la espada se ha roto"; } }
Call y CallStatic
Los métodos __call()
y __callStatic()
son llamados cuando tratas de llamar un método que no existe o inaccesible( private
or protected
) en un objeto. El método __callStatic()
es llamado cuando se trata de llamar un método static y debe ser static.
<?php class Perro{ private $raza; private $nombre; public function __construct($nombre,$raza){ $this->raza = $raza; $this->nombre = $nombre; } private function ladrar(){ echo "waw waw"; } public function __get($property_name){ return $this->$property_name; } public function __call($method_name,$args){ if(method_exists($this,$method_name)){ echo "el metodo es privado"; } else{ echo "<p>tal metodo no existe en el objeto de clase ".__CLASS__."</p>"; } } } $p = new Perro("Yoshi","Terrier"); echo $p->__get('nombre');//obtendra Yoshi $p->ladrar(); $p->nadar();
class SampleClass { public function __call($name, $args) { if ($name == 'multiply') { if (count($args) == 2) { return $args[0] * $args[1]; } else if (count($args) == 3) { return $args[0] * $args[1] * $args[2]; } } // Handle other methods or throw an error if the method is not defined } } $obj = new SampleClass(); echo $obj->multiply(2, 3)."\n"; // Output: 6 echo $obj->multiply(2, 3, 4); // Output: 24
Get value
__get()
– Es utilizado para leer datos inaccesibles (protected , privados o propiedades no existentes )
<?php class Perro{ private $raza; private $nombre; public function __construct($nombre,$raza){ $this->raza = $raza; $this->nombre = $nombre; } public function __get($property_name){ return $this->$property_name; } } $p = new Perro("Yoshi","Terrier"); echo $p->__get('nombre');//obtendra Yoshi
Set value
La verdadera manera en que se asigna un valor a una propiedad inaccesible(private
o protected
) de un objeto es usar __set()
<?php class Perro{ private $raza; private $nombre; public function __construct($nombre,$raza){ $this->raza = $raza; $this->nombre = $nombre; } public function __get($property_name){ return $this->$property_name; } public function __set($property_name,$property_value){ $this->$property_name = $property_value; } } $p = new Perro("Yoshi","Terrier"); $p->__set('nombre','Joker'); echo $p->__get('nombre');//obtendra Joker
isset
Se ejecuta cuando se llama isset()
sobre una propiedad inaccesible o no existente __isset()
,
<?php class Perro{ private $raza; private $nombre= "yoshi"; public function __isset($r){ if(isset($this->$r)){ echo "la propiedad existe \n"; }else{ echo "no hay ninguna propiedad $r iicializada \n"; } } } $p = new Perro(); $p->__isset("nombre"); $p->__isset("raza");
unset(__unset())
Se ejecuta cuando se llama a unset
sobre una propiedad privada(private
), protected
o no existente.
<?php class Perro{ private $raza= "buldog"; private $nombre= "yoshi"; public function __unset($n){ if(isset($this->$n)){ unset($this->$n); } } } $p = new Perro(); $p->__unset("raza"); var_dump($p);
Como puede ver hemos destruido la propiedad $raza del objeto.
Caso de uso, yo diría que este método tiene más sentido que sea utilizado cuando la propiedad sea un array asociativo y queramos sacar uno de sus miembros. El uso que le dimos en el ejemplo no tendría mucho sentido, ya que por algo creamos esa variable en primer lugar o no le veo sentido en este momento, aunque se hace más fácil de ver la funcionalidad del método en este ejemplo.
Serialize(__serialize(), __sleep())
En caso de que un objeto tenga el método __serialize()
o __sleep()
este se ejecuta antes de hacer la serialización.
En este método debemos devolver un array con los nombres de todas las variables del objeto que se va a serializar.
public function __sleep ( void ) : array{ //code //must return an associative array }
__serialize()
public function __serialize ( void ) : array{ //code //must return an associative array }
Probablemente __serialize()
remplace a __sleep()
en un futuro
Normalmente cuando hacemos un serialize a un objeto el resultado es el siguiente
<?php class Perro{ private $raza= "buldog"; private $nombre= "yoshi"; } $p = new Perro(); $x= serialize($p); var_dump($x);
string(81) "O:5:"Perro":2:{s:11:"Perroraza";s:6:"buldog";s:13:"Perronombre";s:5:"yoshi";}"
Como puede estar pensando, pero esto que es perroraza por supuesto esto no es lo que queremos al serializar un objeto.
<?php class Perro{ private $raza= "buldog"; private $nombre= "yoshi"; public function __serialize(): array{ return ["raza","nombre"]; } } $p = new Perro(); $x = serialize($p); var_dump($x);
string(48) "O:5:"Perro":2:{i:0;s:4:"raza";i:1;s:6:"nombre";}"
Si corremos unserialize obtendremos lo siguiente, este asunto de serializar y des-serializar va tomando algo de color.
var_dump(unserialize($x));
object(Perro)#2 (4) { ["raza":"Perro":private]=> string(6) "buldog" ["nombre":"Perro":private]=> string(5) "yoshi" ["0"]=> string(4) "raza" ["1"]=> string(6) "nombre" }
Para entender mas sobre el tema serialize y unserialize recomiendo lea este tema
unserialize (__unserialize() y __wakeup())
Cada vez que se llame un unserialize sobre el objeto, se ejecutara, en caso de que este declarado el metodo __unserialize
y __wakeup
se ejecutara __unserialize
y no __wakeup
.
Este método puede reconstruir cualquier recurso que el objeto pueda tener.
El uso para el que está destinado __wakeup()
es restablecer las conexiones de base de datos que se puedan haber perdido durante la serialización y realizar otras tareas de reinicialización.
__wakeup().
disponible desde PHP 7.4.0__unserialize()
toString (__toString)
Este método es ejecutado cada vez que a nuestro objeto que a nuestro obejto se lo trate de utilizar como un string
__toString example<?php class Perro{ private $raza= "buldog"; private $nombre= "yoshi"; public function __toString(){ echo "raza:".$this->raza."\n nombre:".$this->nombre; } } $p = new Perro(); $p->__toString();
Otro ejemplo para que sea más claro
Ejemplo 2 de __toString<?php class Perro{ private $raza= "buldog"; private $nombre= "yoshi"; public function __toString(){ return "raza:".$this->raza."\n nombre:".$this->nombre; } } $p = new Perro(); echo "".$p;
Invocación
__invoke()
se llama cuando se trata de llamar un objeto como si fuera una función .
<?php class Perro{ private $debug=false; private $raza= "buldog"; private $nombre= "yoshi"; private function ladrar(){ echo "waw waw"; } public function __invoke(){ echo "raza: $this->raza - nombre: $this->nombre"; } } $p = new Perro(); $p();
Set state
Este método static es llamado para las clases exportadas por var_export()
sintaxis
public static function __set_state ( array $properties ) : object{ //code //must return return object }
Clone
Se ejecuta una vez el objeto se ha clonado, si el método __clone()
está definido este será llamado permitiendo cambiar cualquier propiedad que necesite ser cambiada
<?php class Perro{ private $raza; private $nombre; public function __construct($nombre,$raza){ $this->raza = $raza; $this->nombre = $nombre; } private function ladrar(){ echo "waw waw"; } public function __get($property_name){ return $this->$property_name."\n"; } public function __set($property_name, $value){ $this->$property_name = $value; } public function __clone(){ $this->nombre =""; } public function __call($method_name,$args){ if(method_exists($this,$method_name)){ echo "el metodo es privado \n"; } else{ echo "tal metodo no existe en el objeto de clase ".__CLASS__."\n"; } } } $p = new Perro("Yoshi","Terrier"); echo $p->__get('nombre');//obtendra Yoshi $p->ladrar(); $p->nadar(); $p2 = clone $p; echo $p2->__get('nombre'); echo $p2->__get('raza'); echo $p2->__set('nombre','pako'); echo $p2->__get('nombre'); echo $p->__get('nombre'); unset($p); echo $p2->__get("nombre");
El anterior es un ejemplo sencillo, pero deja claro lo que se puede hacer con __clone()
debuginfo
Finalmente, tenemos el método __debuginfo()
, el cual es invocado por var_dump()
Sintaxis:
public function __debugInfo ( void ) : array{ //code //must return an array }
Como puede ver él método debe devolver un array.
Si el método no está definido sobre un objeto, se mostrarán todas las propiedades públicas protegidas y privadas.
class Perro{ private $debug=false; private $raza= "buldog"; private $nombre= "yoshi"; private function ladrar(){ echo "waw waw"; } public function __debuginfo(){ if($this->debug){ $data["properties"]="raza,nombre"; return $data; } else{ return array(get_object_vars ($this),get_class_methods($this)); } } } $p = new Perro(); var_dump($p);