Una clase es un molde para crear objetos. Un objeto es una instancia de una clase:
<?php
class Coche {
// Propiedades (atributos)
public string $marca;
public string $modelo;
public int $velocidad = 0;
// Constructor (se ejecuta al crear el objeto)
public function __construct(string $marca, string $modelo) {
$this->marca = $marca;
$this->modelo = $modelo;
}
// Métodos
public function acelerar(int $km): void {
$this->velocidad += $km;
}
public function frenar(int $km): void {
$this->velocidad = max(0, $this->velocidad - $km);
}
public function estado(): string {
return "{$this->marca} {$this->modelo} → {$this->velocidad} km/h";
}
}
// Crear objetos
$coche1 = new Coche("Seat", "Ibiza");
$coche2 = new Coche("Toyota", "Corolla");
$coche1->acelerar(50);
$coche1->acelerar(30);
$coche1->frenar(20);
echo $coche1->estado(); // Seat Ibiza → 60 km/h
?>
En PHP 8, los parámetros del constructor pueden declarar propiedades directamente:
<?php
class Producto {
public function __construct(
public string $nombre,
public float $precio,
public int $stock = 0
) {}
public function aplicar_descuento(float $porcentaje): void {
$this->precio *= (1 - $porcentaje / 100);
}
}
$p = new Producto("Portátil", 999.99, 10);
echo $p->nombre; // Portátil
$p->aplicar_descuento(10);
echo $p->precio; // 899.991
?>
| Modificador | Desde la clase | Desde clases hijas | Desde fuera |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
Una clase hija hereda métodos y propiedades de la clase padre con extends:
<?php
class Vehiculo {
public function __construct(
protected string $marca,
protected string $modelo
) {}
public function arrancar(): string {
return "{$this->marca} {$this->modelo} arrancando...";
}
}
class Moto extends Vehiculo {
private bool $tieneSidecar = false;
public function __construct(string $marca, string $modelo, bool $sidecar = false) {
parent::__construct($marca, $modelo);
$this->tieneSidecar = $sidecar;
}
public function hacer_caballito(): string {
return "{$this->marca} haciendo caballito! 🏍️";
}
// Override del método padre
public function arrancar(): string {
return parent::arrancar() . " ¡BRUM BRUM!";
}
}
$moto = new Moto("Honda", "CBR600");
echo $moto->arrancar(); // Honda CBR600 arrancando... ¡BRUM BRUM!
echo $moto->hacer_caballito();
?>
No se pueden instanciar directamente, solo sirven como base para otras clases. Definen métodos que las hijas deben implementar:
<?php
abstract class Animal {
protected string $nombre;
public function __construct(string $nombre) {
$this->nombre = $nombre;
}
// Método abstracto: las hijas deben implementarlo
abstract public function sonido(): string;
// Método concreto: se hereda tal cual
public function dormir(): string {
return "{$this->nombre} está durmiendo... zzz";
}
}
class Perro extends Animal {
public function sonido(): string {
return "¡Guau guau!";
}
}
class Gato extends Animal {
public function sonido(): string {
return "¡Miau!";
}
}
$animales = [new Perro("Rex"), new Gato("Garfield")];
foreach ($animales as $a) {
echo $a->sonido() . "\n";
}
?>
Definen un contrato: qué métodos debe tener una clase, pero no cómo se implementan:
<?php
interface Imprimible {
public function imprimir(): string;
}
interface Exportable {
public function exportar(string $formato): string;
}
// Una clase puede implementar múltiples interfaces
class Factura implements Imprimible, Exportable {
public function __construct(
private int $numero,
private float $total
) {}
public function imprimir(): string {
return "Factura #{$this->numero}: {$this->total}€";
}
public function exportar(string $formato): string {
if ($formato === "json") {
return json_encode(["num" => $this->numero, "total" => $this->total]);
}
return $this->imprimir();
}
}
$f = new Factura(123, 450.50);
echo $f->imprimir();
$f instanceof Imprimible; // true
$f instanceof Exportable; // true
?>
Los traits permiten reutilizar métodos entre clases sin herencia (evitando la duplicación de código):
<?php
trait Logeable {
public function log(string $mensaje): void {
echo "[LOG: " . date("Y-m-d H:i:s") . "] $mensaje\n";
}
}
trait Timestampable {
public function getTimestamp(): string {
return date("Y-m-d H:i:s");
}
}
class Usuario {
use Logeable, Timestampable;
public function __construct(private string $nombre) {}
public function guardar(): void {
$this->log("Guardando usuario {$this->nombre}");
// ... lógica de guardado ...
}
}
$user = new Usuario("Ana");
$user->guardar();
echo $user->getTimestamp();
?>
Evitan colisiones de nombres organizando las clases en espacios:
<?php
// Archivo: src/Modelos/Usuario.php
namespace App\Modelos;
class Usuario {
public function __construct(public string $nombre) {}
}
// Archivo: src/Controladores/UsuarioController.php
namespace App\Controladores;
use App\Modelos\Usuario as ModeloUsuario;
class UsuarioController {
public function mostrar(): void {
$user = new ModeloUsuario("Ana");
echo $user->nombre;
}
}
// Archivo: index.php (punto de entrada)
require_once "src/Modelos/Usuario.php";
require_once "src/Controladores/UsuarioController.php";
use App\Controladores\UsuarioController;
$controller = new UsuarioController();
$controller->mostrar();
?>
En lugar de usar require manualmente, Composer carga las clases automáticamente:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
// composer dump-autoload
// En index.php, una sola línea carga todo:
require_once __DIR__ . "/vendor/autoload.php";
use App\Modelos\Usuario;
use App\Controladores\UsuarioController;
$user = new Usuario("Carlos");
?>
Pertenecen a la clase, no a los objetos. Se accede con :::
<?php
class Config {
private static array $valores = [
"db_host" => "localhost",
"db_name" => "tienda"
];
public static function get(string $clave, mixed $default = null): mixed {
return self::$valores[$clave] ?? $default;
}
public static function set(string $clave, mixed $valor): void {
self::$valores[$clave] = $valor;
}
}
echo Config::get("db_host"); // localhost
Config::set("app_version", "1.0");
echo Config::get("app_version"); // 1.0
?>
<?php
class Persona {
private array $datos = [];
// __get: se llama al leer propiedades inaccesibles
public function __get(string $nombre): mixed {
return $this->datos[$nombre] ?? null;
}
// __set: se llama al escribir propiedades inaccesibles
public function __set(string $nombre, mixed $valor): void {
$this->datos[$nombre] = $valor;
}
// __toString: al hacer echo del objeto
public function __toString(): string {
return json_encode($this->datos);
}
// __call: al llamar un método que no existe
public function __call(string $nombre, array $args): mixed {
return "Método '$nombre' no definido";
}
}
$p = new Persona();
$p->nombre = "Ana"; // __set
echo $p->nombre . "\n"; // __get → Ana
echo $p; // __toString
echo $p->metodoInexistente(); // __call
?>
| Característica | Clase abstracta | Interfaz |
|---|---|---|
| Puede tener propiedades | ✅ | ❌ |
| Puede tener métodos con código | ✅ | ❌ (solo firma) |
| Se puede heredar de varias | ❌ (solo una) | ✅ (múltiples) |
| Constructor | ✅ | ❌ |
CuentaBancaria con propiedades titular, saldo y métodos ingresar(), retirar().Figura con un método abstracto area(): float. Implementa Circulo y Rectangulo.JSONSerializable con método toJSON(): string e impleméntala en Articulo.trait para compartir un método validarEmail() entre Usuario y Cliente.