lunes, 5 de agosto de 2013

PHP orientado a objetos - Elementos estáticos

Métodos y atributos estáticos

La palabra reservada static, en PHP y cualquier otro lenguaje de programación, es una declaración que se les da a miembros y/o métodos de una clase. Un miembro o método de una clase declarado como static va a poder ser accedido sin necesidad de hacer una instancia del objecto en una variable. Por lo que va a ser accedido a través de la clase y no a través de un objeto.
La palabra clave static se debe de colocar después de la definición de visibilidad (si no existe la visibilidad es pública) y antes del nombre del atributo o método.

Un método estático es lo más parecido a una función de un lenguaje estructurado. Solo que se encapsula dentro de una clase. Y el hecho de estar en un ámbito de clase y no de objeto tiene dos características:

- Los métodos estáticos no pueden acceder a ningún atributo normal de la clase. Debido a que los métodos estáticos se pueden invocar sin tener creada una instancia del objeto, la variable $this no está disponible dentro de los métodos declarados como estáticos. Pero si que pueden acceder a atributos definidos como estáticos.
- Para acceder a atributos o métodos estáticos, desde el interior de otro método estático, no se necesita una variable que haga referencia a un objeto ($this). En vez de ello se usa la palabra reservada self en conjunción de los caracteres "::" y el nombre del atributo.

 class Prueba {  
   static public $nombre = "José";  
   static public function saludo() {  
     echo "Hola " . self::$nombre . " os saluda";  
   }  
 }  

Y como hemos mencionado que los atributos y método estáticos trabajan en el ámbito de la clase y no de un objeto, para acceder a estos desde el exterior de la clase que los declara, se utiliza el nombre de la clase seguido de ":" y el nombre del atributo o método estático.

 echo Prueba::$nombre;  
 Prueba::saludo();  

Pero evidentemente también podemos crear instancias de clases con elementos estáticos aunque solo podremos acceder a los métodos de la forma que accedíamos hasta ahora.  Un atributo declarado como static no puede ser accedido en un objeto de clase instanciado.

 class Prueba {  
   public static $nombre = "José";  
   public static function saludo() {  
     echo "Hola " . self::$nombre . " os saluda";  
   }  
 }  
 class Prueba2{  
   public function bienvenida(){  
     Prueba::saludo();  
   }  
 }  
 $p = new Prueba();  
 $p2 = new Prueba();  
 $p->saludo();  
 $p2->saludo();  
 $p->$nombre; //error

Utilidad de los atributos y métodos estáticos

- Cuando se trabaja en un proyecto de cierto tamaño es normal que el número de clases y subclases (clases heredadas) sea bastante grande. Y si no se usan elementos estáticos, si un objeto quiere acceder a los atributos o métodos de otro objeto es necesario pasar una instancia de una clase (que tiene que ser declarada) de un objeto a otro.

 class Prueba {  
   public $nombre = "José";  
   public function saludo() {  
     echo "Hola " . $this->nombre . " os saluda";  
   }  
 }  
 class Prueba2{  
   public function bienvenida($prueba){  
     $prueba->saludo();  
   }  
 }  
 $prueba = new Prueba();  
 $p = new Prueba2();  
 $p->bienvenida($prueba);  

Pero esto es muy incómodo y además propicia a que el código se convierta en algo engorroso.
Gracias a que los elementos estáticos son accesibles desde cualquier contexto no es necesario el hecho de ir pasando referencias de objetos de un lado a otro.

 class Prueba {  
   public static $nombre = "José";  
   public static function saludo() {  
     echo "Hola " . self::$nombre . " os saluda";  
   }  
 }  
 class Prueba2{  
   public function bienvenida(){  
     Prueba::saludo();  
   }  
 }  
 $p = new Prueba2();  
 $p->bienvenida();  

- Un atributo estático está disponible en cualquier instancia de una clase, por lo tanto se pueden modificar valores estáticos para que estén disponibles para todos los miembros de un tipo.

 class Prueba {  
   public static $nombre = "José";  
   public static function saludo() {  
     echo "Hola " . self::$nombre . " os saluda";  
   }  
 }  
 $p = new Prueba();  
 $p2 = new Prueba();  
 Prueba::$nombre = "Juan";  
 $p->saludo();  //Hola Juan os saluda
 $p2->saludo();  //Hola Juan os saluda


Herencia y el enlace estático en tiempo de ejecución

Hasta PHP 5.3 había una limitación con la herencia y lo métodos estáticos. Los métodos estáticos se heredan, pero palabras clave como self no se resuelven en tiempo de ejecución. Lo que ocasiona que siempre apuntan a las clases en las se definen los métodos que utilizan dichas palabras claves y no a las clases herederas.

 class Padre {  
   public static function nombre() {  
     echo __CLASS__;  
   }  
   public static function test() {  
     self::nombre(); 
   }  
 }  
 class Hijo extends Padre {  
   public static function nombre() {  
     echo __CLASS__;  
   }  
 }  
 Hijo::test();  //Padre

 class Record {  
   protected static $nombreTabla = 'base';  
   public static function getNombreTabla() {  
     return self::$nombreTabla;  
   }  
 }  
 class User extends Record {  
   protected static $nombreTabla = 'usuario';  
 }  
 echo User::getNombreTabla(); // base  

Por lo tanto hasta PHP 5.3 la solución era copiar y pegar las mismas funciones en las clases herederas o convertir los métodos estáticos a dinámicos (no estáticos), y utilizar $this ya que este se comporta como se espera. Pero a partir de dicha versión aparece el enlace estático en tiempo de ejecución. Con lo que se introduce un nuevo uso para la palabra static. Permitir obtener el valor de la clase donde se está ejecutando en lugar de la clase base de la cual hereda.

 class Record {  
   protected static $nombreTabla = 'base';  
   public static function getNombreTabla() {  
     return static::$nombreTabla;  
   }  
 }  
 class User extends Record {  
   protected static $nombreTabla = 'usuario';  
 }  
 echo User::getNombreTabla(); // usuario 


Entradas relacionadas

PHP orientado a objetos - Introducción

PHP orientado a objetos - Herencia

2 comentarios: