Tabla de Contenidos
Programación Orientada a Objetos en PHP
Notas sobre el uso de Programación Orientada a Objetos en PHP.
PHP incluye soporte para Programación Orientada a Objetos desde la versión 5.0
includes a complete object model. Some of its features are: visibility, abstract and final classes and methods, additional magic methods, interfaces, and cloning.
PHP treats objects in the same way as references or handles, meaning that each variable contains an object reference rather than a copy of the entire object. See Objects and References
Crear una clase
Para definir una clase se utiliza la palabra reservada class seguida del nombre de la clase, seguido de llaves que encerrará la definición de propiedades y métodos pertenecientes a dicha clase:
class Persona { // Propiedades // Métodos }
El nombre de la clase sigue las normas de nombrado de PHP: puede empezar con una letra o guion bajo seguido de cualquier número de letras, números o guiones bajos.
Crear un método
Igual que las funciones, para definir un método se utiliza la palabra reservada function:
class Persona { function hablar() { echo "Hola!"; } }
Crear una instancia
Instanciar una clase es crear un objeto. El objeto tendrá las propiedades y métodos definidos en la clase.
Para crear una instancia utilizamos la palabra reservada new.
class Persona { function hablar() { return "Hola"; } } // Creación de instancias $fulanito = new Persona(); $menganito = new Persona();
Para llamar a un método utilizamos el operador →:
echo "Fulanito dice: " . $fulanito->hablar(); echo "Menganito dice: " . $menganito->hablar();
Crear propiedades
Las propiedades son las variables propias de una clase.
class Persona { public $nombre; public $edad; }
En versiones antiguas de PHP se usaba la palabra reservada var antes de declarar las propiedades.
Si declaramos una propiedad sin un modificador de visibilidad/acceso, esta será declarada como public.
Para acceder a las propiedades se usa el operador →.
$fulanito = new Persona(); $fulanito->nombre = "Fulanito"; $fulanito->edad = "18";
Por medio de $this podemos hacer referencia a las propiedades y métodos dentro de la propia clase.
class Persona { public $nombre; public $edad; function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } }
Crear un constructor
Un constructor es una función que se ejecuta cuando se crea un objeto, así que es útil para recibir parámetros iniciales o ejecutar acciones o métodos cuando se “construye” el objeto. El constructor es la palabra reservada __construct:
class Persona { private $nombre; private $edad; public function __construct($nombre, $edad) { $this->nombre = $nombre $this->edad = $edad; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } }
Cuando creemos el objeto, podremos indicarle los parámetros:
$fulanito = new Persona("Fulanito", 18); echo $fulanito->hablar();
Promocionar las propiedades del constructor
Antes de la versión 8, los parámetros del constructor se definen primero dentro de la clase, luego dentro del constructor y finalamente se asignan unos a otros:
class Punto { public float $x; public float $y; public float $z; public function __construct(float $x = 0, float $y = 0, $z = 0) { $this->x = $x; $this->y = $y; $this->z = $z; } }
Desde PHP 8 podemos abreviar:
class Punto { public function __construct(public float $x = 0, public float $y = 0, public $z = 0) { $this->x = $x; $this->y = $y; $this->z = $z; } }
Crear una función destructora
Generalmente no sería necesario encargarse de destruir un objeto cuando no se usa ya que este trabajo es automático por parte del recolector de basura de PHP. Una función destructura se ejecuta cuando el objeto deja de existir, ya sea porque termina el script o porque usamos la función unset(). La función destructora en PHP es __destruct:
class Persona { private $nombre; private $edad; public function __construct($nombre, $edad) { $this->nombre = $nombre $this->edad = $edad; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } function __destruct() { echo $this->nombre . " me suicido"; } }
Cuando creemos el objeto, al terminar nuestro script veremos qué sucede:
$fulanito = new Persona("Fulanito", 18); echo $fulanito->hablar(); // Ahora se mostrará por pantalla: "Fulanito se suicida"
Si queremos forzar la destrucción del objeto en cualquier momento del script, usamos la función unset():
$fulanito = new Persona("Fulanito", 18); echo $fulanito->hablar(); unset($fulanito); // Ahora se mostrará por pantalla: "Fulanito se suicida"
La ventaja de usar unset() para indicar explícitamente que queremos destruir un objeto es que ejecutará lo que hayamos definido en el método __destruct.
Herencia
Se trata de uno de los pilares de la programación orientada a objetos. Con la herencia se crea la jerarquía de clases.
En PHP se realiza con la palabra reservada extends:
class Trabajador extends Persona { }
La clase Trabajador hereda todo lo que tenga la clase Persona:
$benito = new Trabajador("Benito", 20); echo $benito->hablar();
Modificadores de acceso
Un concepto fundamental en la Programación Orientada a Objetos es el encapsulamiento, es decir, que dentro de una clase vayan tanto datos como acciones. De esta manera, otro usuario (desarrollador u otro código) solo podrá tener acceso a lo que nosotros queramos, todo lo demás estará “encapsulado” o restringido.
Los modificadores de acceso permiten definir qué propiedades y métodos se podrán usar desde otras clases o scripts. En PHP tenemos los siguientes:
- public: acceso total. Es el valor por defecto si no se indica.
- private: solo acceso desde dentro de la clase.
- protected: acceso dentro de la clase y dede de las heredadas.
class Persona { private $nombre; private $edad; public function __construct($nombre, $edad) { $this->nombre = $nombre $this->edad = $edad; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } }
Tras haber definido las propiedades como privadas, no podríamos hacer lo siguiente:
$fulanito = new Persona("Fulanito", 18); echo $fulanito->edad; // Esto no funcionaría porque no tenemos acceso desde fuera de la clase
El error sería algo como:
Fatal error: Cannot access private property Persona::$edad in Persona.php on line XXX
Para limitarlo a las clases heredadas, usamos protected:
class Persona { protected $nombre; protected $edad; public function __construct($nombre, $edad) { $this->nombre = $nombre $this->edad = $edad; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } }
En la clase heredada:
class Trabajador extends Persona { public function hablarTrabajador() { return "Soy trabajador y tengo " . $this->edad . "años"; } }
Pero si hubiésemos definido las propiedades de la clase Persona como private, al hacer esto:
$fulanito = new Trabajador("Fulanito", 18); echo $fulanito->hablarTrabajador();
Obtendríamos un error al acceder a la propiedad edad:
Notice: Undefined property: Trabajador::$edad in Trabajador.php on line XX
Setters y getters
Otra herramienta que nos permite manejar el encapsulamiento de las clases es realizar las funciones getters y setters que no es más que funciones que manipularán las propiedades de la clase. Las propiedades siempre deberían estar como privadas o protegidas para evitar que se acceda accidental o intencionalmente desde el exterior.
class Persona { protected $nombre; private $edad; private $corbata = "No"; public function __construct($nombre, $edad) { $this->nombre = $nombre $this->edad = $edad; } // Setter public function set_corbata($corbata) { $this->corbata = $corbata; } // Getter public function get_corbata() { return $this->corbata; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años"; } public function tiene_corbata() { return $this->nombre " " . $this->corbata . " tiene corbata"; } }
Los usamos:
$fulanito = new Persona("Fulanito", 18); $fulanito->set_corbata("Sí"); echo $fulanito->tiene_corbata(); // Devolverá: // Fulanito Sí tiene corbata
Métodos mágicos
Los métodos mágicos son un tipo especial de métodos que sobrescriben la acción por defecto de PHP cuando se realizan ciertas acciones sobre un objeto.
__set y __get
__set: permite escribir datos sobre propiedades inaccesibles (protegidas o privadas).__get: Permite consultar datos sobre propiedades inaccesibles (protegidas o privadas)
PHP reserva los dos guiones bajos seguidos para los métodos mágicos. __construct y __destruct son también métodos mágicos
class Persona { private $nombre; private $edad; private $corbata = "no"; public function __construct($nombre, $edad) { $this->nombre = $nombre; $this->edad = $edad; } public function __destruct() { echo $this->nombre . " se suicida"; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años" . PHP_EOL; } // Ojo, lleva ''$'' la propiedad function __set($name, $value) { $this->$name = $valor; } // Ojo, lleva ''$'' la propiedad function __get($name) { return $this->$name; } public function set_corbata($corbata) { $this->corbata = $corbata; } public function get_corbata() { return $this->corbata; } public function tiene_corbata() { return $this->nombre . " " . $this->corbata . " tiene corbata"; } }
Para usarlo:
$fulanito = new Persona("Fulanito", 18); //$fulanito->set_corbata("Sí"); $fulanito->corbata = "No"; echo $fulanito->tiene_corbata();
Pudimos cambiar la propiedad privada sin problema.
Con el __set podríamos hacer la modificación de cualquiera de las propiedades:
$fulanito = new Persona("Fulanito", 18); $fulanito->corbata = "No"; $fulanito->nombre = "Menganito"; $fulanito->edad = 20;
Conversión de una clase a cadena (__toString)
Por medio de la función __toString() podemos tener una cadena como salida de una instancia de una clase.
class Gato { private $nombre; private $pelo; public function __construct($nombre, $pelo) { $this->nombre = $nombre; $this->pelo = $pelo; } public function __toString() { return "Mi nombre es " . $this->nombre . " y el color de mi pelo es " . $this->pelo; } }
$benito = new Gato("Benito", "azul"); echo $benito; // Mostrará "Mi nombre es benito y el color de mi pelo es azul"
Métodos y propiedades estáticas
Un modificador de acceso de mucha utilidad es static que permite utilizar métodos y propiedades sin necesidad de crear instancias de la clase.
El operador de resolución de ámbito / alcance (::) permite acceder a elementos estáticos, constantes y sobrescribir propiedades o métodos de una clase.
class Persona { public static $clave = "12345"; private $nombre; private $edad; private $corbata = "no"; public function __construct($nombre, $edad) { $this->nombre = $nombre; $this->edad = $edad; } public function __destruct() { echo $this->nombre . " se suicida"; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años" . PHP_EOL; } // Ojo, lleva ''$'' la propiedad function __set($name, $value) { $this->$name = $value; } // Ojo, lleva ''$'' la propiedad function __get($name) { return $this->$name; } public function set_corbata($corbata) { $this->corbata = $corbata; } public function get_corbata() { return $this->corbata; } public function tiene_corbata() { return $this->nombre . " " . $this->corbata . " tiene corbata"; } }
Sin instanciar la clase (crear el objeto), podremos acceder a la propiedad estática
echo "La clave secreta es " . Persona::$clave";
Lo mismo pasaría con los métodos estáticos:
class Persona { // Atributos // Métodos public static function mensaje_secreto() { return "Haz el bien sin mirar a quien"; } }
Usamos:
echo "La frase secreta es " . Persona::mensaje_secreto();
Operador de resolución de alcance
Este operador nos permite acceder a métodos y propiedades estáticas.
self: sustituye a$thiscuando llamamos a propiedades estáticas.parent: la utilizamos cuando queremos llamar desde la clase hija las propiedades o métodos de la clase padre.
self
class Persona { public static $clave = "12354"; private $nombre; private $edad; private $corbata = "no"; public function __construct($nombre, $edad) { $this->nombre = $nombre; $this->edad = $edad; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años; la clave secreta es " . $this->clave. PHP_EOL; }
PHP nos mostrará un error por haber utilizado $this→clave para acceder a una propiedad estática.
La solución es usar self:
public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años; la clave secreta es " . self::$clave. PHP_EOL; }
parent
Para el ejemplo de parent, vamos a coger una clase hija:
class Trabajador extends Persona { public function hablar() { parent::hablar(); // método hablar() de la clase padre return "Yo soy la clase hija"; } }
Constantes
El operador :: también nos permite acceder a constantes:
class Persona { const MAYORIA_EDAD = 18; public static $clave = "12354"; private $nombre; private $edad; private $corbata = "no"; // código }
Y para llamar esa constante fuera de la clase:
echo "La mayoría de edad en España es a los " . Persona::MAYORIA_EDAD . " años";
Sobrescribir métodos (overriding)
Práctica muy común en la programación orientada a objetos. Al crear una nueva clase a partir de otra (herencia), podemos hacer 3 cosas:
- Añadir nuevas propiedades y métodos.
- Eliminar propiedades y métodos.
- Modificar propiedades y métodos.
Partimos de una clase:
class Persona { public static $clave = "12354"; private $nombre; private $edad; private $corbata = "no"; public function __construct($nombre, $edad) { $this->nombre = $nombre; $this->edad = $edad; } public function __destruct() { echo $this->nombre . " se suicida"; } public function hablar() { return "Me llamo " . $this->nombre . " y tengo " . $this->edad . " años" . PHP_EOL; } // Ojo, lleva ''$'' la propiedad function __set($name, $value) { $this->$name = $value; } // Ojo, lleva ''$'' la propiedad function __get($name) { return $this->$name; } public function set_corbata($corbata) { $this->corbata = $corbata; } public function get_corbata() { return $this->corbata; } public function tiene_corbata() { return $this->nombre . " " . $this->corbata . " tiene corbata"; } }
Creamos una clase hija:
class Trabajador extends Persona { // Estamos sobrescribiendo un método existente en la clase padre: public function hablar() { return "Soy el currante " . $this->nombre . " y tengo " . $this->edad . " años" . PHP_EOL; } }
Al llamar al método hablar() usará el de la clase hija:
$fulanito = new Trabajador("Fulanito", 18); echo $fulanito->hablar(); // Devuelve: Soy trabajador y tengo 18 años
Evitar sobrescritura
Por medio de la palabra reservada final evitamos que la clase derivada sobrescriba un método.
Una clase también puede ser final y no se permitirá que se extendiera o heredara a otra clase.
Esto normalmente no se usa mucho ya que uno de los objetivos de la programación orientada a objetos es que puedan trabajar varias personas sobre la misma base de código. Si impedimos que algunas clases interesantes se puedan heredar o sobrescribir sus métodos, acabaríamos muy probablemente duplicando código.
class Gato { public function maullar() { echo "Miau"; } final public function ronronear() { echo "rrrrr"; } }
Clase heredada:
class GatoVolador extends Gato { public function ronronear() { echo "ronroneo"; } }
Y la usamos:
$gato = new GatoVolador();
Nos daría el siguiente error:
Fatal error: Cannot override final method Gato::ronronear() in Clase.php on line X
Clonar objetos
Cuando queremos duplicar un objeto, el operador de igualdad (=) no sirve porque copia la referencia al objeto.
// Copiar por valor $a = 10; $b = 10; // tanto $a como $b valdrán lo mismo, pero si se modifica $a, no afecta al valor de $b // Copiar por referencia $objeto1 = $objeto2; $objeto1->setNombre("objetoUno"); echo $objeto1->getNombre(); echo $objeto2->getNombre(); // saldría lo mismo que en $objeto1 $objeto2->setNombre("objetoDos"); echo $objeto1->getNombre(); // saldría "objetoDos" echo $objeto2->getNombre(); // saldría "objetoDos"
Para duplicar un objeto usamos la palabra reservada clone.
$fulanito = new Persona("Fulanito", 19); $menganito = clone $fulanito; $menganito->setNombre("Menganito"); echo $fulanito->getNombre(); // Fulanito echo $menganito->getNombre(); // Menganito
Manejo de constantes en clases
- Las constantes se diferencian de las variables en que no es necesario el símbolo de dólar para definirlas ni para usarlas.
- El valor de una constante no puede ser una variable, función o expresión
- El valor de la constante es la misma para todas las instancias, no se pueden modificar.
Recordamos que para llamar una constante dentro de una clase usamos self:
class Persona { const EDAD = 18; function edadPersona() { return self::EDAD } }
$persona = new Persona(); echo "La mayoría de edad en España es " . Persona::EDAD; echo "La mayoría de edad en España es " . $persona->edadPersona();
Manejo de clases en archivos externos
La práctica más común es tener un archivo por clase. Podemos llamarlas con los comandos include, include_once, require y require_once.
require_once "PersonaClass.php"; class Trabajador extends Persona { // Código }
Carga automática de clases
Al crear un fichero por clase, cuando vamos a usar muchas, una de las mayores molestias es tener que hacer una larga lista de inclusiones al comienzo de cada script (uno por cada clase). A partir de la versión 5.0.0 de PHP se pueden hacer autocargas para evitar el uso de include y require.
La función spl_autoload_register() permite autocargar las clases que llamemos en nuestro programa:
spl_autoload_register(function ($nombre_clase) { require_once "clases" . DIRECTORY_SEPARATOR . $nombre_clase . '.php'; }); $persona = new Persona("Fulanito", 19);
Gracias a esto, cada vez que detecte el uso de new, PHP buscará y cargará la clase adecuada dentro de la carpeta clases.
La práctica recomendada sería ir cargando las clases que necesitemos en lugar de no tener el control
Crear clases abstractas
- Las clases abstractas no pueden ser instanciadas.
- Pueden declarar la existencia de los métodos, pero no su implementación.
- Pueden contener métodos no abstractos, pero deben contener al menos un método abstracto.
- Los métodos abstractos deben ser definidos en las clases heredadas, pero deben tener la misma visibilidad.
- La implementación debe tener la misma “firma”: la declaración de tipos y el número de argumentos.
- Las clases abstractas están implementadas desde la versión 5.4.0.
Las clases abstractas se suelen usar en proyectos grandes.
Para crear una clase abstracta se emplea la palabra reservada abstract:
abstract class Mamifero { // Método abstracto abstract public function saludo(); // Método no abstracto public function maullar() { return "Miau, miau"; } }
Esta clase no la podemos instanciar, pero sí heredar:
require_once "Mamifero.php"; class Gato extends Mamifero { public function saludo() { return "Hola, mundo"; } }
$felix = new Gato(); echo "Saludo: " . $felix->saludo(); echo "Maullido: " . $felix->maullar();
Crear interfaces
- Las interfaces permiten crear código con el cual especificar qué métodos deben ser implementados por una clase (prototipos) sin definir su implementación
- Los métodos declarados en una interfaz deben ser públicos.
- Las interfaces se definen con la palabra reservada
interface. - Para implementar una interfaz desde una clase se utiliza la palabra reservada
implements - Una clase puede implementar más de una interfaz si se desea.
Las interfaces en proyectos pequeños no suponen una ventaja, pero en proyectos medianos, grandes o muy grandes es de enorme importancia para comunicarse entre desarrolladores, son como un mapa en un viaje.
interface iMamifero { public function andar(); public function ruido(); }
Implementamos un par de clases (observar el uso de la palabra reservada implements):
class Gato implements iMamifero { public function andar() { return "camina"; } public function ruido() { return "miau, miau"; } }
class Delfin implements iMamifero { public function andar() { return "nada"; } public function ruido() { return "iu, iu"; } }
Instanciamos:
$felix = new Gato(); $flipper = new Delfin(); echo "El gato " . $felix->andar() . " y dice " . $felix->ruido(); echo "El delfín" . $flipper->andar() . " y dice " . $flipper->ruido();
Iteración de objetos
Desde la versión 5 podemos iterar las propiedades visibles o públicas de una clase.
class Gato { public $peso = 10; private $genero = "M"; protected $edad = 0; public function iteracion() { echo "Iteración dentro de la clase" . PHP_EOL; foreach ($this as $clave => $valor) { echo $clave . " -> " . $valor . PHP_EOL; } } }
Creamos el objeto:
$gato = new Gato(); $gato->iteracion();
Nos devolverá algo como:
Iteración dentro de la clase peso -> 10 genero -> M edad -> 0
Como hemos visto, al hacer la iteración dentro de la clase no importa los modificadores de acceso que hayamos usado con las propiedades. Si la iteración la hacemos fuera de la clase:
$gato = new Gato(); echo "Iteración fuera de la clase" . PHP_EOL; foreach ($gato as $clave => $valor) { echo $clave . " - > " . $valor . PHP_EOL; }
Nos devolvería:
Iteración fuera de la clase peso - > 10
Porque solo la propiedad peso es pública.
Comparación de objetos
Dos instancias de una clase son iguales si tienen los mismos atributos y valores (los valores se comparan con el operador de igualdad, =)
Cuando se utiliza el operador de identidad (===), las variables de un objeto son idénticas sí y solo sí hacen referencia a la misma instancia de la misma clase.
class Gato { public $bandera; } class Perro { public $bandera; }
Comparamos:
$gato1 = new Gato(); $gato2 = new Gato(); $gato3 = $gato1; // copia por referencia $perro1 = new Perro(); echo "Comparamos elementos de la misma clase" . PHP_EOL; echo "gato1 == gato"; echo ($gato1 == $gato2) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 != gato"; echo ($gato1 != $gato2) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 === gato"; echo ($gato1 === $gato2) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 !== gato"; echo ($gato1 !== $gato2) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL;
Resultado:
Comparamos elementos de la misma clase gato1 == gato Verdadero gato1 != gato Falso gato1 === gato Falso gato1 !== gato Verdadero
Compararemos elementos de la misma clase a la misma referencia:
echo "Compararemos elementos de la misma clase a la misma referencia:" . PHP_EOL; echo "gato1 == gato3"; echo ($gato1 == $gato3) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 != gato3"; echo ($gato1 != $gato3) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 === gato3"; echo ($gato1 === $gato3) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 !== gato3"; echo ($gato1 !== $gato3) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL;
Resultado:
Compararemos elementos de la misma clase a la misma referencia: gato1 == gato3 Verdadero gato1 != gato3 Falso gato1 === gato3 Verdadero gato1 !== gato3 Falso
Finalmente, compararemos elementos de diferente clase (aunque tengan los mismos elementos):
echo "Compararemos elementos de distinta clase, pero mismas propiedades:" . PHP_EOL; echo "gato1 == perro1"; echo ($gato1 == $perro1) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 != perro1"; echo ($gato1 != $perro1) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 === perro1"; echo ($gato1 === $perro1) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL; echo "gato1 !== perro1"; echo ($gato1 !== $perro1) ? " Verdadero" . PHP_EOL: " Falso" . PHP_EOL;
Resultado:
Compararemos elementos de la misma clase a la misma referencia: gato1 == perro1 Falso gato1 != perro1 Verdadero gato1 === perro1 Falso gato1 !== perro1 Verdadero
Clases anónimas
Las clases anónimas no tienen nombre. Disponibles desde la versión 7 de PHP.
Las clases anónimas son útiles para definir objetos sencillos y desechables.
$gato = new Class("Don Gato") { private $nombre; public function __construct($nombre) { $this->nombre = $nombre; } public function getNombre() { return "Mi nombre es " . $this->nombre; } }; echo $gato->getNombre(); // Mi nombre es Don Gato
Traits (rasgos)
Por medio de los traits podemos reducir las limitaciones de la herencia simple (solo se puede heredar una clase).
Podemos reutilizar código (conjuntos de métodos) sobre clases independientes o a jerarquía de clase diferentes.
Un rasgo o trait es similar a las clases, pero solo agrupa métodos específicos.
interface Animal { function nacer(); function crecer(); function reproducir(); function morir(); } abstract class Vertebrado implements Animal { private $huesos; public function getHuesos() { return $this->huesos; } public function nacer() {}; public function crecer() {}; public function reproducir() {}; public function morir() {}; } abstract class Invertebrado implements Animal { private $hemocianina; public function getHemocianina() { return $this->hemocianina; } public function nacer() {}; public function crecer() {}; public function reproducir() {}; public function morir() {}; } class Molusco extends Invertebrado { private $radula; public function getRadula() { return $this->radula; } } class Reptil extends Invertebrado { private $escamas; public function getEscamas() { return $this->escamas; } }
Tanto los moluscos como los reptiles son ovíparos. Si creásemos una clase abstracta Ovíparo, no podríamos heredar de ella porque ya están heredando de Vertebrado y de Invertebrado (PHP solo permite herencia simple). Para este caso se usan los traits:
trait Oviparo { private $huevos; public function getHuevos() { return $this->huevos; } }
Ahora podríamos hacer:
class Reptil extends Invertebrado { use Oviparo; // Usamos el trait para simular herencia múltiple private $escamas; public function getEscamas() { return $this->escamas; } }
Si vamos a usar varios traits los separaríamos con comas: use trait1, trait2, trait3;
Si ahora creamos una nueva clase:
class Tortuga extends Reptil { }
Herederá de Vertebrado, Reptil y Ovíparo.
A la creación de interfaz, clase abstracta y clases concretas es lo que se conoce como modelado en programación orientada a objetos.
La clase ReflectionClass
Devuelve información sobre una clase.
$reflection = new ReflectionClass("Tortuga"); Reflexion::export($reflexion); // Devuelve información sobre la clase var_dump($reflexion->isAbstract()); // Es abstracta? var_dump($reflexion->isInstantiable()); // Se puede instanciar? var_dump($reflexion->isInternal()); // Es interna?
Constantes predefinidas
- __FILE__
- __LINE__
- __CLASS__
- __TRAIT__
- __METHOD__
- __NAMESPACE__
Espacios de nombres
Cuando tenemos varias clases, funciones, etc. Es común que queramos compartirlos (crear una biblioteca) y puede ocurrir que hayamos usado el mismo nombre que otras funciones o clases y al querer usarlas se produce un conflicto porque no se puede distinguir cuál es cuál.
Los espacios de nombres (namespaces) son una forma de “encapsular” elementos de tal manera que se puedan distinguir de otros que podrían llamarse igual.
Una analogía es el sistema de archivos de un disco duro:
/home/tempwin/docs/letra.txt /home/tempwin/music/artista/album/letra.txt
Hay dos ficheros con el mismo nombre (letra.txt), pero están en ubicaciones diferentes por lo cual no crean conflicto.
En PHP se utilizan los espacios de nombres para la creación de bibliotecas evitando el conflicto de nombre de funciones o variables y para simplificar nombres de elementos muy largos y evitar el primer problema en el desarrollo de código reutilizable.
Los espacios de nombres están disponibles en PHP desde 5.3.0
Los espacios de nombres nos permiten agrupar clases, interfaces, funciones y constantes relacionadas por lo general en una biblioteca.
Creación
El código que se ve afectado por el espacio de nombres son las clases, interfaces, funciones y constantes.
Se definen con la palabra reservada namespace, que debe definirse antes de cualquier sentencia y no estar precedida por ningún espacio ni caracter (excepto comentarios).
namespace Perro; const NOMBRE = "Bobby"; class MiPerro { // Código public function pasearPerro() { // Código } }
Podemos crear subespacios de nombres como si de directorios se tratase:
namespace Perro\salchicha; const NOMBRE = "Bobby"; class MiPerro { // Código public function pasearPerro() { // Código } }
También podemos definir varios namespaces en un mismo fichero:
namespace Perro { const NOMBRE = "Bobby"; class MiPerro { // Código public function pasearPerro() { // Código } } } namespace Gato { const NOMBRE = "Garfield"; class MiGato { // Código public function pasearGato() { // Código } } }
Llamada a un espacio de nombres
El manejo de los espacios de nombres es muy similar al uso de un sistema de ficheros de un disco duro:
- Llamar al archivo sin indicar ruta
- Llamar al archivo con una ruta relativa
- Llamar al archivo con una ruta absoluta
Creamos el namespace (fichero1.php):
namespace Animal\Perro\salchica; const NOMBRE = "Cinnamon"; function comer () { echo "Estoy comiendo, Cinnamon"; } class Pasear { static function paseo() { echo "Estoy paseando, Cinnamon"; } }
En otro fichero (fichero2.php):
namespace Animal\Perro; include "fichero1.php"; const NOMBRE = "Perro"; function comer () { echo "Estoy comiendo, perro"; } class Pasear { static function paseo() { echo "Estoy paseando, perro"; } }
Si ahora hacemos lo siguiente:
// Nombre no cualificado comer(); Pasear::paseo(); echo NOMBRE;
Se usa el del fichero2.php.
Si ahora hacemos:
// Nombre cualificado salchicha\comer(); salchicha\Pasear::paseo(); echo salchicha\NOMBRE;
Se usará el del fichero1.php
Finalmente:
// Nombre completamente cualificado \Animal\Perro\salchicha\comer(); \Animal\Perro\salchicha\Pasear::paseo(); echo \Animal\Perro\salchicha\NOMBRE;
La constante __NAMESPACE__ contiene el espacio de nombres actual.
Alias con use
Desde PHP 5.3.0 se ofrecen varias formas de crear alias para apodar una clase, interfaz o espacio de nombres.
Empleamos la palabra reservada use.
Por ejemplo, suponiendo que tenemos un fichero PHP con el namespace Animales\Mamiferos, en un nuevo fichero podemos crear un alias del mismo de la siguiente manera:
use Animales\Mamiferos as mascota; use function Animales\Mamiferos\ladrar as ladrido; use const Animales\Mamiferos\PERRO as DOG; $perro = new mascota\Perro; ladrido(); echo DOG;
Espacio de nombres global
Puede ocurrir que definamos una función o clase que se llame igual que una interna de PHP. Para distinguir la interna, usamos \:
namespace MiNamespace\miSubNamespace; function fopen($archivo) { $f = \fopen($archivo); return $f; } class Exception extends \Exception {}; $e = new Exception("Hola"); // la clase del namespace $e2 = new \Exception("Hola, otra vez"); // La clase global
Funciones interesantes
get_declared_classes(): devuelve un array con el nombre de las clases definidas en nuestra instalación de PHP.class_exists(): indica si la clase pasada por argumento está definida.get_class_methods(): muestra los métodos de una clase.method_exists(): indica si un método existe en una clase específica.get_class(): indica la clase a la que pertenece la instancia. También se puede usar$objeto::class.is_a(): indica si una instancia pertenece a cierta clase.get_parent_class(): devuelve la clase origen o padre de la clase solicitada.is_subclass_of(): indica si las clases están en la misma jerarquía de clase.
