Blog de programación, errores, soluciones

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 metodos son llamados sin la necesidad de ser llamados por usted, por eso su nombre de metodos magicos.

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.

EN CONSTRUCCIÓN: esta entrada esta en construcción y puede que tenga información que no es correcta mientras este en construcción en ese caso puede contactarme en twitter @luisg2249_luis

IMPORTANTE: Todos los métodos mágicos deben ser públicos.

Métodos mágicos

constructor
destructor
call y callstatic
getvalue
setvalue
isset
unset
serialize, sleep
unserialize, wakeap
tostring
invoke
setstate
clone
debuginfo

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

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.

un ejemplo de __call()

<?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();

Get value

Es utilizado para leer datos inaccesibles (protected , privados o propiedades no existentes )(__get())

<?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 unset sobre una propiedad privada, 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 que un objeto tenga el metodo __serialize() o __sleep() este se ejecuta antes de hacer la serializacion.

En este método debemos devolver un array con los nombres de todas las variables del objeto que se va a serializar.

__serialize()(disponible desde PHP 7.4.0), __sleep().

public function __serialize ( void ) : array{
   //code
   //must return an associative array
}
public function __sleep ( 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

http://php.net/manual/en/language.oop5.serialization.php

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.

__unserialize()(disponible desde PHP 7.4.0), __wakeup().

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

<?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

<?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

Este se llama cuando se trata de llamar un objeto como si fuera una función __invoke().

<?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 metodo __debuginfo(), el cual es invocado por var_dump()

Sintaxis:

public function __debugInfo ( void ) : array{
   //code
   //must return an array
}

Como puede ver el meto 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);
1
2

Somethig wrong? If you found an error or mistake on the content you can contact me on twitter | @luisg2249_luis.
Last 4 post in same category