Blog de programación, errores, soluciones

Chose Language:
Author: Admin/Publisher |finished | checked

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.

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

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.

construct example
<?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.

destruct example
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.

__call() example
<?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();
estos 2 métodos mágicos por si no se ha dado cuenta también los puede usar para hacer overloading(sobrecarga) a los métodos según su nombre y argumentos
Ejemplo de sobrecarga con __call
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 )

__get() example
<?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()

__set() example
<?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(),

__isset() example
<?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.

__unset() example
<?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.

__sleep()
public function __sleep ( void ) : array{
   //code
   //must return an associative array
}
disponible desde PHP 7.4.0

__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

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.

__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 .

__invoke() example
<?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

__clone() example
<?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.

__debuginfo() example
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);

Category: php
Something wrong? If you found an error or mistake in the content you can contact me on Twitter | @luisg2249_luis.
Last 4 post in same category