Tabla de contenido
- Repaso rápido: clases y objetos
- El constructor
- Encapsulamiento: public, private y protected
- Getters y Setters
- Herencia con extends
- La palabra clave parent
- Sobrescribir métodos (Override)
En el artículo anterior vimos los conceptos básicos de la Programación Orientada a Objetos: qué son las clases, atributos y métodos. Ahora profundizamos en dos pilares fundamentales: el encapsulamiento y la herencia. Con estos dos conceptos ya puedes estructurar código PHP de forma profesional.
Repaso rápido: clases y objetos
Una clase es como un molde. Un objeto es la instancia concreta creada a partir de ese molde:
<?php
class Usuario {
public string $nombre;
public string $email;
public function saludar(): string {
return "Hola, soy " . $this->nombre;
}
}
$usuario = new Usuario();
$usuario->nombre = "Abraham";
$usuario->email = "hola@8devmx.com";
echo $usuario->saludar(); // "Hola, soy Abraham"
?>
El constructor
El constructor es un método especial que se ejecuta automáticamente cuando creas un nuevo objeto. Te permite inicializar los atributos al momento de la creación:
<?php
class Usuario {
public string $nombre;
public string $email;
public string $ciudad;
public function __construct(string $nombre, string $email, string $ciudad = "Cancún") {
$this->nombre = $nombre;
$this->email = $email;
$this->ciudad = $ciudad;
}
public function presentarse(): string {
return "Soy {$this->nombre}, vivo en {$this->ciudad}.";
}
}
$usuario1 = new Usuario("Abraham", "hola@8devmx.com");
$usuario2 = new Usuario("Brenda", "brenda@ejemplo.com", "Mérida");
echo $usuario1->presentarse(); // "Soy Abraham, vivo en Cancún."
echo $usuario2->presentarse(); // "Soy Brenda, vivo en Mérida."
?>
El parámetro $ciudad = "Cancún" es opcional con valor por defecto.
Encapsulamiento: public, private y protected
El encapsulamiento controla quién puede acceder a los atributos y métodos de una clase. En PHP hay tres niveles de acceso:
public: accesible desde cualquier lugarprivate: solo accesible desde dentro de la misma claseprotected: accesible desde la clase y sus clases hijas
<?php
class CuentaBancaria {
public string $titular;
private float $saldo; // solo esta clase puede tocarlo
protected string $moneda; // esta clase y sus hijas
public function __construct(string $titular, float $saldo, string $moneda = "MXN") {
$this->titular = $titular;
$this->saldo = $saldo;
$this->moneda = $moneda;
}
public function getSaldo(): float {
return $this->saldo;
}
public function depositar(float $monto): void {
if ($monto > 0) {
$this->saldo += $monto;
}
}
public function retirar(float $monto): bool {
if ($monto > 0 && $monto <= $this->saldo) {
$this->saldo -= $monto;
return true;
}
return false;
}
}
$cuenta = new CuentaBancaria("Abraham", 5000.00);
echo $cuenta->getSaldo(); // 5000
$cuenta->depositar(1000);
echo $cuenta->getSaldo(); // 6000
// Esto daría ERROR (saldo es private):
// echo $cuenta->saldo;
?>
Getters y Setters
Cuando un atributo es private pero necesitas acceder o modificarlo desde fuera, usas getters (para leer) y setters (para escribir):
<?php
class Producto {
private string $nombre;
private float $precio;
public function __construct(string $nombre, float $precio) {
$this->nombre = $nombre;
$this->setPrecio($precio); // usamos el setter para validar
}
// Getter
public function getNombre(): string {
return $this->nombre;
}
// Getter
public function getPrecio(): float {
return $this->precio;
}
// Setter con validación
public function setPrecio(float $precio): void {
if ($precio < 0) {
throw new InvalidArgumentException("El precio no puede ser negativo");
}
$this->precio = $precio;
}
public function getPrecioFormateado(): string {
return "$ " . number_format($this->precio, 2) . " {MXN}";
}
}
$producto = new Producto("Laptop", 15999.99);
echo $producto->getNombre(); // Laptop
echo $producto->getPrecioFormateado(); // $ 15,999.99 {MXN}
$producto->setPrecio(14500.00);
echo $producto->getPrecio(); // 14500
?>
La ventaja del setter: puedes agregar validaciones y lógica sin que quien usa la clase tenga que preocuparse por ello.
Herencia con extends
La herencia permite que una clase “herede” atributos y métodos de otra clase. La clase padre se llama superclase o clase base, y la que hereda se llama subclase o clase hija.
<?php
// Clase padre (superclase)
class Animal {
public string $nombre;
public string $especie;
public function __construct(string $nombre, string $especie) {
$this->nombre = $nombre;
$this->especie = $especie;
}
public function describir(): string {
return "{$this->nombre} es un {$this->especie}.";
}
public function hacerSonido(): string {
return "...";
}
}
// Clase hija — hereda todo de Animal
class Perro extends Animal {
public string $raza;
public function __construct(string $nombre, string $raza) {
parent::__construct($nombre, "perro"); // llamamos al constructor del padre
$this->raza = $raza;
}
public function hacerSonido(): string {
return "¡Guau!";
}
public function traer(): string {
return "{$this->nombre} va a buscar la pelota.";
}
}
class Gato extends Animal {
public function __construct(string $nombre) {
parent::__construct($nombre, "gato");
}
public function hacerSonido(): string {
return "¡Miau!";
}
}
$perro = new Perro("Rex", "Labrador");
$gato = new Gato("Misu");
echo $perro->describir(); // "Rex es un perro." (heredado del padre)
echo $perro->hacerSonido(); // "¡Guau!" (sobreescrito)
echo $perro->traer(); // "Rex va a buscar la pelota." (propio)
echo $gato->describir(); // "Misu es un gato."
echo $gato->hacerSonido(); // "¡Miau!"
?>
La palabra clave parent
parent:: se usa para llamar a métodos de la clase padre desde la clase hija:
<?php
class Empleado extends Usuario {
public string $puesto;
public float $salario;
public function __construct(
string $nombre,
string $email,
string $puesto,
float $salario
) {
parent::__construct($nombre, $email); // construye la parte de Usuario
$this->puesto = $puesto;
$this->salario = $salario;
}
public function presentarse(): string {
$intro = parent::presentarse(); // usa la presentación del padre
return $intro . " Trabajo como {$this->puesto}.";
}
}
?>
Sobrescribir métodos (Override)
Cuando una clase hija define un método con el mismo nombre que el padre, sobrescribe el comportamiento original:
<?php
class Notificacion {
public function enviar(string $mensaje): void {
echo "Enviando: $mensaje";
}
}
class NotificacionEmail extends Notificacion {
private string $destinatario;
public function __construct(string $destinatario) {
$this->destinatario = $destinatario;
}
public function enviar(string $mensaje): void {
// sobreescribe el método del padre
echo "Enviando email a {$this->destinatario}: $mensaje";
}
}
class NotificacionSMS extends Notificacion {
private string $telefono;
public function __construct(string $telefono) {
$this->telefono = $telefono;
}
public function enviar(string $mensaje): void {
echo "Enviando SMS al {$this->telefono}: $mensaje";
}
}
$notificaciones = [
new NotificacionEmail("hola@8devmx.com"),
new NotificacionSMS("+529981234567"),
];
foreach ($notificaciones as $notif) {
$notif->enviar("¡Tu pedido está listo!");
}
?>
Aquí vemos algo importante: podemos tratar objetos de tipos diferentes de forma uniforme porque todos heredan de Notificacion. A esto se le llama polimorfismo.
El encapsulamiento y la herencia son los pilares que hacen que la POO valga la pena. Una vez que los dominas, tu código se vuelve mucho más organizado, reutilizable y fácil de mantener.