PHP Orientado a Objetos¶
Duración y criterios de evaluación
Duración estimada: 18 sesiones
Resultado de aprendizaje:
- Desarrolla aplicaciones Web identificando y aplicando mecanismos para separar el código de presentación de la lógica de negocio.
Criterios de evaluación:
- Se han identificado las ventajas de separar la lógica de negocio de los aspectos de presentación de la aplicación.
- Se han analizado tecnologías y mecanismos que permiten realizar esta separación y sus características principales.
- Se han utilizado objetos y controles en el servidor para generar el aspecto visual de la aplicación web en el cliente.
- Se han utilizado formularios generados de forma dinámica para responder a los eventos de la aplicación Web.
- Se han escrito aplicaciones Web con mantenimiento de estado y separación de la lógica de negocio.
- Se han aplicado los principios de la programación orientada a objetos.
- Se ha probado y documentado el código.
Clases y Objetos¶
PHP sigue un paradigma de programación orientada a objetos (POO) basada en clases.
Un clase es un plantilla que define las propiedades y métodos para poder crear objetos. De este manera, un objeto es una instancia de una clase.
Tanto las propiedades como los métodos se definen con una visibilidad (quien puede acceder)
- Privado -
private
: Sólo puede acceder la propia clase. - Protegido -
protected
: Sólo puede acceder la propia clase o sus descendientes. - Público -
public
: Puede acceder cualquier otra clase.
Para declarar una clase, se utiliza la palabra clave class
seguido del nombre de la clase. Para instanciar un objeto a partir de la clase, se utiliza new
:
<?php
class NombreClase {
// propiedades
// y métodos
}
$ob = new NombreClase();
Clases con mayúscula
Todas las clases empiezan por letra mayúscula.
Cuando un proyecto crece, es normal modelar las clases mediante UML (¿recordáis Entornos de Desarrollo?). La clases se representan mediante un cuadrado, separando el nombre, de las propiedades y los métodos:
Una vez que hemos creado un objeto, se utiliza el operador ->
para acceder a una propiedad o un método:
$objeto->propiedad;
$objeto->método(parámetros);
Si desde dentro de la clase, queremos acceder a una propiedad o método de la misma clase, utilizaremos la referencia $this
;
$this->propiedad;
$this->método(parámetros);
Así pues, como ejemplo, codificaríamos una persona en el fichero Persona.php
como:
<?php
class Persona {
private string $nombre;
public function setNombre(string $nom) {
$this->nombre=$nom;
}
public function imprimir(){
echo $this->nombre;
echo '<br>';
}
}
$bruno = new Persona(); // creamos un objeto
$bruno->setNombre("Bruno Díaz");
$bruno->imprimir();
Aunque se pueden declarar varias clases en el mismo archivo, es una mala práctica. Así pues, cada fichero contedrá una sola clase, y se nombrará con el nombre de la clase.
Encapsulación¶
Las propiedades se definen privadas o protegidas (si queremos que las clases heredadas puedan acceder).
Para cada propiedad, se añaden métodos públicos (getter/setter):
public setPropiedad(tipo $param)
public getPropiedad() : tipo
Las constantes se definen públicas para que sean accesibles por todos los recursos.
<?php
class MayorMenor {
private int $mayor;
private int $menor;
public function setMayor(int $may) {
$this->mayor = $may;
}
public function setMenor(int $men) {
$this->menor = $men;
}
public function getMayor() : int {
return $this->mayor;
}
public function getMenor() : int {
return $this->menor;
}
}
Recibiendo y enviando objetos¶
Es recomendable indicarlo en el tipo de parámetros. Si el objeto puede devolver nulos se pone ?
delante del nombre de la clase.
Objetos por referencia
Los objetos que se envían y reciben como parámetros siempre se pasan por referencia.
<?php
function maymen(array $numeros) : ?MayorMenor {
$a = max($numeros);
$b = min($numeros);
$result = new MayorMenor();
$result->setMayor($a);
$result->setMenor($b);
return $result;
}
$resultado = maymen([1,76,9,388,41,39,25,97,22]);
echo "<br>Mayor: ".$resultado->getMayor();
echo "<br>Menor: ".$resultado->getMenor();
Constructor¶
El constructor de los objetos se define mediante el método mágico __construct
.
Puede o no tener parámetros, pero sólo puede haber un único constructor.
<?php
class Persona {
private string $nombre;
public function __construct(string $nom) {
$this->nombre = $nom;
}
public function imprimir(){
echo $this->nombre;
echo '<br>';
}
}
$bruno = new Persona("Bruno Díaz");
$bruno->imprimir();
Constructores en PHP 8¶
Una de las grandes novedades que ofrece PHP 8 es la simplificación de los constructores con parámetros, lo que se conoce como promoción de las propiedades del constructor.
Para ello, en vez de tener que declarar las propiedades como privadas o protegidas, y luego dentro del constructor tener que asignar los parámetros a estás propiedades, el propio constructor promociona las propiedades.
Veámoslo mejor con un ejemplo. Imaginemos una clase Punto
donde queramos almacenar sus coordenadas:
<?php
class Punto {
protected float $x;
protected float $y;
protected float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
En PHP 8, quedaría del siguiente modo (mucho más corto, lo que facilita su legibilidad):
<?php
class Punto {
public function __construct(
protected float $x = 0.0,
protected float $y = 0.0,
protected float $z = 0.0,
) {}
}
El orden importa
A la hora de codificar el orden de los elementos debe ser:
<?php
declare(strict_types=1);
class NombreClase {
// propiedades
// constructor
// getters - setters
// resto de métodos
}
?>
Clases estáticas¶
Son aquellas que tienen propiedades y/o métodos estáticos (también se conocen como de clase, por que su valor se comparte entre todas las instancias de la misma clase).
Se declaran con static
y se referencian con ::
.
- Si queremos acceder a un método estático, se antepone el nombre de la clase:
Producto::nuevoProducto()
. - Si desde un método queremos acceder a una propiedad estática de la misma clase, se utiliza la referencia
self
:self::$numProductos
<?php
class Producto {
const IVA = 0.23;
private static $numProductos = 0;
public static function nuevoProducto() {
self::$numProductos++;
}
}
Producto::nuevoProducto();
$impuesto = Producto::IVA;
También podemos tener clases normales que tengan alguna propiedad estática:
<?php
class Producto {
const IVA = 0.23;
private static $numProductos = 0;
private $codigo;
public function __construct(string $cod) {
self::$numProductos++;
$this->codigo = $cod;
}
public function mostrarResumen() : string {
return "El producto ".$this->codigo." es el número ".self::$numProductos;
}
}
$prod1 = new Producto("PS5");
$prod2 = new Producto("XBOX Series X");
$prod3 = new Producto("Nintendo Switch");
echo $prod3->mostrarResumen();
Introspección¶
Al trabajar con clases y objetos, existen un conjunto de funciones ya definidas por el lenguaje que permiten obtener información sobre los objetos:
instanceof
: permite comprobar si un objeto es de una determinada claseget_class
: devuelve el nombre de la claseget_declared_class
: devuelve un array con los nombres de las clases definidasclass_alias
: crea un aliasclass_exists
/method_exists
/property_exists
:true
si la clase / método / propiedad está definidaget_class_methods
/get_class_vars
/get_object_vars
: Devuelve un array con los nombres de los métodos / propiedades de una clase / propiedades de un objeto que son accesibles desde dónde se hace la llamada.
Un ejemplo de estas funciones puede ser el siguiente:
<?php
$p = new Producto("PS5");
if ($p instanceof Producto) {
echo "Es un producto";
echo "La clase es ".get_class($p);
class_alias("Producto", "Articulo");
$c = new Articulo("Nintendo Switch");
echo "Un articulo es un ".get_class($c);
print_r(get_class_methods("Producto"));
print_r(get_class_vars("Producto"));
print_r(get_object_vars($p));
if (method_exists($p, "mostrarResumen")) {
$p->mostrarResumen();
}
}
Clonado
Al asignar dos objetos no se copian, se crea una nueva referencia. Si queremos una copia, hay que clonarlo mediante el método clone(object) : object
Si queremos modificar el clonado por defecto, hay que definir el método mágico __clone()
que se llamará después de copiar todas las propiedades.
Más información en https://www.php.net/manual/es/language.oop5.cloning.php
Herencia¶
PHP soporta herencia simple, de manera que una clase solo puede heredar de otra, no de dos clases a la vez. Para ello se utiliza la palabra clave extends
. Si queremos que la clase A hereda de la clase B haremos:
class A extends B
El hijo hereda los atributos y métodos públicos y protegidos.
Cada clase en un archivo
Como ya hemos comentado, deberíamos colocar cada clase en un archivo diferente para posteriormente utilizarlo mediante include
. En los siguiente ejemplo los hemos colocado junto para facilitar su legibilidad.
Por ejemplo, tenemos una clase Producto
y una Tv
que hereda de Producto
:
<?php
class Producto {
public $codigo;
public $nombre;
public $nombreCorto;
public $PVP;
public function mostrarResumen() {
echo "<p>Prod:".$this->codigo."</p>";
}
}
class Tv extends Producto {
public $pulgadas;
public $tecnologia;
}
Podemos utilizar las siguientes funciones para averiguar si hay relación entre dos clases:
get_parent_class(object): string
is_subclass_of(object, string): bool
<?php
$t = new Tv();
$t->codigo = 33;
if ($t instanceof Producto) {
echo $t->mostrarResumen();
}
$padre = get_parent_class($t);
echo "<br>La clase padre es: " . $padre;
$objetoPadre = new $padre;
echo $objetoPadre->mostrarResumen();
if (is_subclass_of($t, 'Producto')) {
echo "<br>Soy un hijo de Producto";
}
Sobreescribir métodos¶
Podemos crear métodos en los hijos con el mismo nombre que el padre, cambiando su comportamiento.
Para invocar a los métodos del padre -> parent::nombreMetodo()
<?php
class Tv extends Producto {
public $pulgadas;
public $tecnologia;
public function mostrarResumen() {
parent::mostrarResumen();
echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
}
}
Constructor en hijos¶
En los hijos no se crea ningún constructor de manera automática. Por lo que si no lo hay, se invoca automáticamente al del padre. En cambio, si lo definimos en el hijo, hemos de invocar al del padre de manera explícita.
<?php
class Producto {
public string $codigo;
public function __construct(string $codigo) {
$this->codigo = $codigo;
}
public function mostrarResumen() {
echo "<p>Prod:".$this->codigo."</p>";
}
}
class Tv extends Producto {
public $pulgadas;
public $tecnologia;
public function __construct(string $codigo, int $pulgadas, string $tecnologia) {
parent::__construct($codigo);
$this->pulgadas = $pulgadas;
$this->tecnologia = $tecnologia;
}
public function mostrarResumen() {
parent::mostrarResumen();
echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
}
}
<?php
class Producto {
public function __construct(private string $codigo) { }
public function mostrarResumen() {
echo "<p>Prod:".$this->codigo."</p>";
}
}
class Tv extends Producto {
public function __construct(
string $codigo,
private int $pulgadas,
private string $tecnologia)
{
parent::__construct($codigo);
}
public function mostrarResumen() {
parent::mostrarResumen();
echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
}
}
Clases abstractas¶
Las clases abstractas obligan a heredar de una clase, ya que no se permite su instanciación. Se define mediante abstract class NombreClase {
.
Una clase abstracta puede contener propiedades y métodos no-abstractos, y/o métodos abstractos.
<?php
// Clase abstracta
abstract class Producto {
private $codigo;
public function getCodigo() : string {
return $this->codigo;
}
// Método abstracto
abstract public function mostrarResumen();
}
Cuando una clase hereda de una clase abstracta, obligatoriamente debe implementar los métodos que tiene el padre marcados como abstractos.
<?php
class Tv extends Producto {
public $pulgadas;
public $tecnologia;
public function mostrarResumen() { //obligado a implementarlo
echo "<p>Código ".$this->getCodigo()."</p>";
echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
}
}
$t = new Tv();
echo $t->getCodigo();
Clases finales¶
Son clases opuestas a abstractas, ya que evitan que se pueda heredar una clase o método para sobreescribirlo.
<?php
class Producto {
private $codigo;
public function getCodigo() : string {
return $this->codigo;
}
final public function mostrarResumen() : string {
return "Producto ".$this->codigo;
}
}
// No podremos heredar de Microondas
final class Microondas extends Producto {
private $potencia;
public function getPotencia() : int {
return $this->potencia;
}
// No podemos implementar mostrarResumen()
}
Interfaces¶
Permite definir un contrato con las firmas de los métodos a cumplir. Así pues, sólo contiene declaraciones de funciones y todas deben ser públicas.
Se declaran con la palabra clave interface
y luego las clases que cumplan el contrato lo realizan mediante la palabra clave implements
.
<?php
interface Nombreable {
// declaración de funciones
}
class NombreClase implements NombreInterfaz {
// código de la clase
Se permite la herencia de interfaces. Además, una clase puede implementar varios interfaces (en este caso, sí soporta la herecia múltiple, pero sólo de interfaces).
<?php
interface Mostrable {
public function mostrarResumen() : string;
}
interface MostrableTodo extends Mostrable {
public function mostrarTodo() : string;
}
interface Facturable {
public function generarFactura() : string;
}
class Producto implements MostrableTodo, Facturable {
// Implementaciones de los métodos
// Obligatoriamente deberá implementar public function mostrarResumen, mostrarTodo y generarFactura
}
Métodos encadenados¶
Sigue el planteamiento de la programación funcional, y también se conoce como method chaining. Plantea que sobre un objeto se realizan varias llamadas.
<?php
$p1 = new Libro();
$p1->setNombre("Harry Potter");
$p1->setAutor("JK Rowling");
echo $p1;
// Method chaining
$p2 = new Libro();
$p2->setNombre("Patria")->setAutor("Aramburu");
echo $p2;
Para facilitarlo, vamos a modificar todos sus métodos mutadores (que modifican datos, setters, ...) para que devuelvan una referencia a $this
:
<?php
class Libro {
private string $nombre;
private string $autor;
public function getNombre() : string {
return $this->nombre;
}
public function setNombre(string $nombre) : Libro {
$this->nombre = $nombre;
return $this;
}
public function getAutor() : string {
return $this->autor;
}
public function setAutor(string $autor) : Libro {
$this->autor = $autor;
return $this;
}
public function __toString() : string {
return $this->nombre." de ".$this->autor;
}
}
Métodos mágicos¶
Todas las clases PHP ofrecen un conjunto de métodos, también conocidos como magic methods que se pueden sobreescribir para sustituir su comportamiento. Algunos de ellos ya los hemos utilizado.
Ante cualquier duda, es conveniente consultar la documentación oficial.
Los más destacables son:
__construct()
__destruct()
→ se invoca al perder la referencia. Se utiliza para cerrar una conexión a la BD, cerrar un fichero, ...__toString()
→ representación del objeto como cadena. Es decir, cuando hacemosecho $objeto
se ejecuta automáticamente este método.__get(propiedad)
,__set(propiedad, valor)
→ Permitiría acceder a las propiedad privadas, aunque siempre es más legible/mantenible codificar los getter/setter.__isset(propiedad)
,__unset(propiedad)
→ Permite averiguar o quitar el valor a una propiedad.__sleep()
,__wakeup()
→ Se ejecutan al recuperar (unserialize^) o almacenar un objeto que se serializa (serialize), y se utilizan para permite definir qué propiedades se serializan.__call()
,__callStatic()
→ Se ejecutan al llamar a un método que no es público. Permiten sobrecargan métodos.
Espacio de nombres¶
Desde PHP 5.3 y también conocidos como Namespaces, permiten organizar las clases/interfaces, funciones y/o constantes de forma similar a los paquetes en Java.
Recomendación
Un sólo namespace por archivo y crear una estructura de carpetas respectando los niveles/subniveles (igual que se hace en Java)
Se declaran en la primera línea mediante la palabra clave namespace
seguida del nombre del espacio de nombres asignado (cada subnivel se separa con la barra invertida \
):
Por ejemplo, para colocar la clase Producto
dentro del namespace Dwes\Ejemplos
lo haríamos así:
<?php
namespace Dwes\Ejemplos;
const IVA = 0.21;
class Producto {
public $nombre;
public function muestra() : void {
echo"<p>Prod:" . $this->nombre . "</p>";
}
}
Acceso¶
Para referenciar a un recurso que contiene un namespace, primero hemos de tenerlo disponible haciendo uso de include
o require
. Si el recurso está en el mismo namespace, se realiza un acceso directo (se conoce como acceso sin cualificar).
Realmente hay tres tipos de acceso:
- sin cualificar:
recurso
- cualificado:
rutaRelativa\recurso
→ no hace falta poner el namespace completo - totalmente cualificado:
\rutaAbsoluta\recurso
<?php
namespace Dwes\Ejemplos;
include_once("Producto.php");
echo IVA; // sin cualificar
echo Utilidades\IVA; // acceso cualificado. Daría error, no existe \Dwes\Ejemplos\Utilidades\IVA
echo \Dwes\Ejemplos\IVA; // totalmente cualificado
$p1 = new Producto(); // lo busca en el mismo namespace y encuentra \Dwes\Ejemplos\Producto
$p2 = new Model\Producto(); // daría error, no existe el namespace Model. Está buscando \Dwes\Ejemplos\Model\Producto
$p3 = new \Dwes\Ejemplos\Producto(); // \Dwes\Ejemplos\Producto
Para evitar la referencia cualificada podemos declarar el uso mediante use
(similar a hacer import
en Java). Se hace en la cabecera, tras el namespace
:
Los tipos posibles son:
use const nombreCualificadoConstante
use function nombreCualificadoFuncion
use nombreCualificadoClase
use nombreCualificadoClase as NuevoNombre
// para renombrar elementos
Por ejemplo, si queremos utilizar la clase \Dwes\Ejemplos\Producto
desde un recurso que se encuentra en la raíz, por ejemplo en inicio.php
, haríamos:
<?php
include_once("Dwes\Ejemplo\Producto.php");
use const Dwes\Ejemplos\IVA;
use \Dwes\Ejemplos\Producto;
echo IVA;
$p1 = new Producto();
To use
or not to use
En resumen, use
permite acceder sin cualificar a recursos que están en otro namespace. Si estamos en el mismo espacio de nombre, no necesitamos use
.
Organización¶
Todo proyecto, conforme crece, necesita organizar su código fuente. Se plantea una organización en la que los archivos que interactuan con el navegador se colocan en el raíz, y las clases que definamos van dentro de un namespace (y dentro de su propia carpeta src
o app
).

Organización, includes y usos
- Colocaremos cada recurso en un fichero aparte.
- En la primera línea indicaremos su namespace (si no está en el raíz).
- Si utilizamos otros recursos, haremos un
include_once
de esos recursos (clases, interfaces, etc...). - Cada recurso debe incluir todos los otros recursos que referencie: la clase de la que hereda, interfaces que implementa, clases utilizadas/recibidas como parámetros, etc...
- Si los recursos están en un espacio de nombres diferente al que estamos, emplearemos
use
con la ruta completa para luego utilizar referencias sin cualificar.
Autoload¶
¿No es tedioso tener que hacer el include
de las clases? El autoload viene al rescate.
Así pues, permite cargar las clases (no las constantes ni las funciones) que se van a utilizar y evitar tener que hacer el include_once
de cada una de ellas. Para ello, se utiliza la función spl_autoload_register
<?php
spl_autoload_register( function( $nombreClase ) {
include_once $nombreClase.'.php';
} );
?>
¿Por qué se llaman autoload?
Porque antes se realizaba mediante el método mágico __autoload()
, el cual está deprecated desde PHP 7.2
Y ¿cómo organizamos ahora nuestro código aprovechando el autoload?

Para facilitar la búsqueda de los recursos a incluir, es recomendable colocar todas las clases dentro de una misma carpeta. Nosotros la vamos a colocar dentro de app
(más adelante, cuando estudiemos Laravel veremos el motivo de esta decisión). Otras carpetas que podemos crear son test
para colocar las pruebas PhpUnit que luego realizaremos, o la carpeta vendor
donde se almacenarán las librerías del proyecto (esta carpeta es un estándard dentro de PHP, ya que Composer la crea automáticamente).
Como hemos colocado todos nuestros recursos dentro de app
, ahora nuestro autoload.php
(el cual colocamos en la carpeta raíz) sólo va a buscar dentro de esa carpeta:
<?php
spl_autoload_register( function( $nombreClase ) {
include_once "app/".$nombreClase.'.php';
} );
?>
Autoload y rutas erróneas
En Ubuntu al hacer el include de la clase que recibe como parámetro, las barras de los namespace (\
) son diferentes a las de las rutas (/
). Por ello, es mejor que utilicemos el fichero autoload:
<?php
spl_autoload_register( function( $nombreClase ) {
$ruta = "app\\".$nombreClase.'.php';
$ruta = str_replace("\\", "/", $ruta); // Sustituimos las barras
include_once $ruta';
} );
?>
Gestión de Errores¶
PHP clasifica los errores que ocurren en diferentes niveles. Cada nivel se identifica con una constante. Por ejemplo:
E_ERROR
: errores fatales, no recuperables. Se interrumpe el script.E_WARNING
: advertencias en tiempo de ejecución. El script no se interrumpe.E_NOTICE
: avisos en tiempo de ejecución.
Podéis comprobar el listado completo de constantes de https://www.php.net/manual/es/errorfunc.constants.php
Para la configuración de los errores podemos hacerlo de dos formas:
- A nivel de
php.ini
:error_reporting
: indica los niveles de errores a notificarerror_reporting = E_ALL & ~E_NOTICE
-> Todos los errores menos los avisos en tiempo de ejecución.
display_errors
: indica si mostrar o no los errores por pantalla. En entornos de producción es común ponerlo aoff
- mediante código con las siguientes funciones:
error_reporting(codigo)
-> Controla qué errores notificarset_error_handler(nombreManejador)
-> Indica que función se invocará cada vez que se encuentre un error. El manejador recibe como parámetros el nivel del error y el mensaje
A continuación tenemos un ejemplo mediante código:
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
$resultado = $dividendo / $divisor;
error_reporting(E_ALL & ~E_NOTICE);
set_error_handler("miManejadorErrores");
$resultado = $dividendo / $divisor;
restore_error_handler(); // vuelve al anterior
function miManejadorErrores($nivel, $mensaje) {
switch($nivel) {
case E_WARNING:
echo "<strong>Warning</strong>: $mensaje.<br/>";
break;
default:
echo "Error de tipo no especificado: $mensaje.<br/>";
}
}
Error de tipo no especificado: Undefined variable: dividendo.
Error de tipo no especificado: Undefined variable: divisor.
Error de tipo Warning: Division by zero.
Excepciones¶
La gestión de excepciones forma parte desde PHP 5. Su funcionamiento es similar a Java, haciendo uso de un bloque try / catch / finally
.
Si detectamos una situación anómala y queremos lanzar una excepción, deberemos realizar throw new Exception
(adjuntando el mensaje que lo ha provocado).
<?php
try {
if ($divisor == 0) {
throw new Exception("División por cero.");
}
$resultado = $dividendo / $divisor;
} catch (Exception $e) {
echo "Se ha producido el siguiente error: ".$e->getMessage();
}
La clase Exception
es la clase padre de todas las excepciones. Su constructor recibe mensaje[,codigoError][,excepcionPrevia]
.
A partir de un objeto Exception
, podemos acceder a los métodos getMessage()
y getCode()
para obtener el mensaje y el código de error de la excepción capturada.
El propio lenguaje ofrece un conjunto de excepciones ya definidas, las cuales podemos capturar (y lanzar desde PHP 7). Se recomienda su consulta en la documentación oficial.
Creando excepciones¶
Para crear una excepción, la forma más corta es crear una clase que únicamente herede de Exception
.
<?php
class HolaExcepcion extends Exception {}
Si queremos, y es recomendable dependiendo de los requisitos, podemos sobrecargar los métodos mágicos, por ejemplo, sobrecargando el constructor y llamando al constructor del padre, o rescribir el método __toString
para cambiar su mensaje:
<?php
class MiExcepcion extends Exception {
public function __construct($msj, $codigo = 0, Exception $previa = null) {
// código propio
parent::__construct($msj, $codigo, $previa);
}
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function miFuncion() {
echo "Una función personalizada para este tipo de excepción\n";
}
}
Si definimos una excepción de aplicación dentro de un namespace, cuando referenciemos a Exception
, deberemos referenciarla mediante su nombre totalmente cualificado (\Exception
), o utilizando use
:
<?php
namespace \Dwes\Ejemplos;
class AppExcepcion extends \Exception {}
```
=== "Mediante `use`"
``` php
<?php
namespace \Dwes\Ejemplos;
use Exception;
class AppExcepcion extends Exception {}
```
### Excepciones múltiples
Se pueden usar excepciones múltiples para comprobar diferentes condiciones. A la hora de capturarlas, se hace de más específica a más general.
``` php
<?php
$email = "ejemplo@ejemplo.com";
try {
// Comprueba si el email es válido
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
throw new MiExcepcion($email);
}
// Comprueba la palabra ejemplo en la dirección email
if(strpos($email, "ejemplo") !== FALSE) {
throw new Exception("$email es un email de ejemplo no válido");
}
} catch (MiExcepcion $e) {
echo $e->miFuncion();
} catch(Exception $e) {
echo $e->getMessage();
}
Autoevaluación
¿Qué pasaría al ejectuar el siguiente código?
<?php
class MainException extends Exception {}
class SubException extends MainException {}
try {
throw new SubException("Lanzada SubException");
} catch (MainException $e) {
echo "Capturada MainException " . $e->getMessage();
} catch (SubException $e) {
echo "Capturada SubException " . $e->getMessage();
} catch (Exception $e) {
echo "Capturada Exception " . $e->getMessage();
}
```
Si en el mismo `catch` queremos capturar varias excepciones, hemos de utilizar el operador `|`:
``` php
<?php
class MainException extends Exception {}
class SubException extends MainException {}
try {
throw new SubException("Lanzada SubException");
} catch (MainException | SubException $e ) {
echo "Capturada Exception " . $e->getMessage();
}
Desde PHP 7, existe el tipo Throwable
, el cual es un interfaz que implementan tanto los errores como las excepciones, y nos permite capturar los dos tipos a la vez:
<?php
try {
// tu codigo
} catch (Throwable $e) {
echo 'Forma de capturar errores y excepciones a la vez';
}
Si sólo queremos capturar los errores fatales, podemos hacer uso de la clase Error
:
<?php
try {
// Genera una notificación que no se captura
echo $variableNoAsignada;
// Error fatal que se captura
funcionQueNoExiste();
} catch (Error $e) {
echo "Error capturado: " . $e->getMessage();
}
Relanzar excepciones¶
En las aplicaciones reales, es muy común capturar una excepción de sistema y lanzar una de aplicación que hemos definido nostros.
También podemos lanzar las excepciones sin necesidad de estar dentro de un try/catch
.
<?php
class AppException extends Exception {}
try {
// Código de negocio que falla
} catch (Exception $e) {
throw new AppException("AppException: ".$e->getMessage(), $e->getCode(), $e);
}
SPL¶
Standard PHP Library es el conjunto de funciones y utilidades que ofrece PHP, como:
- Estructuras de datos
- Pila, cola, cola de prioridad, lista doblemente enlazada, etc...
- Conjunto de iteradores diseñados para recorrer estructuras agregadas
- arrays, resultados de bases de datos, árboles XML, listados de directorios, etc.
Podéis consultar la documentación en https://www.php.net/manual/es/book.spl.php o ver algunos ejemplos en https://diego.com.es/tutorial-de-la-libreria-spl-de-php
También define un conjunto de excepciones que podemos utilizar para que las lancen nuestras aplicaciones:
LogicException
(extends Exception
)BadFunctionCallException
BadMethodCallException
DomainException
InvalidArgumentException
LengthException
OutOfRangeException
RuntimeException
(extends Exception
)OutOfBoundsException
OverflowException
RangeException
UnderflowException
UnexpectedValueException
También podéis consultar la documentación de estas excepciones en https://www.php.net/manual/es/spl.exceptions.php.