Hasta ahora trabajamos con PHP utilizando variables, funciones, formularios, PDO, sesiones, autenticación y seguridad. El siguiente paso es aprender programación orientada a objetos, una forma de organizar el código en clases y objetos.
Este enfoque es fundamental para trabajar en proyectos medianos o grandes y es la base de muchos frameworks modernos, incluido Laravel.
¿Qué es la programación orientada a objetos?
La programación orientada a objetos, también llamada POO u OOP, organiza el código alrededor de objetos que representan entidades del sistema.
Por ejemplo, en una aplicación podemos tener objetos como:
- Usuario;
- Producto;
- Pedido;
- Curso;
- Factura.
Cada objeto puede tener datos y comportamientos propios.
¿Qué es una clase?
Una clase funciona como una plantilla para crear objetos.
<?php class Usuario { } ?>
Crear un objeto
Para crear una instancia utilizamos new:
$usuario = new Usuario();
$usuario es ahora un objeto de la clase Usuario.
Propiedades
Las propiedades representan los datos del objeto.
class Usuario
{
public string $nombre;
public string $email;
}
Podemos asignar valores:
$usuario = new Usuario(); $usuario->nombre = 'Ana'; $usuario->email = 'ana@email.com';
Métodos
Los métodos representan comportamientos del objeto.
class Usuario { public string $nombre; public function saludar(): string { return 'Hola, soy ' . $this->nombre; } }
Para llamarlo:
$usuario = new Usuario(); $usuario->nombre = 'Ana'; echo $usuario->saludar();
¿Qué significa $this?
$this hace referencia al objeto actual.
Por ejemplo:
$this->nombre
significa “la propiedad nombre de este objeto”.
El constructor
El constructor se ejecuta automáticamente al crear un objeto.
class Usuario { public string $nombre; public string $email; public function __construct(string $nombre, string $email) { $this->nombre = $nombre; $this->email = $email; } }
Ahora podemos crear el objeto directamente con sus datos:
$usuario = new Usuario( 'Ana', 'ana@email.com' );
Promoción de propiedades en el constructor
En versiones modernas de PHP podemos simplificar el código:
class Usuario { public function __construct( public string $nombre, public string $email ) { } }
Visibilidad: public, protected y private
Las propiedades y métodos pueden tener distintos niveles de acceso.
public
Puede utilizarse desde cualquier parte:
public string $nombre;
private
Solo puede utilizarse dentro de la misma clase:
private string $password;
protected
Puede utilizarse dentro de la clase y sus clases hijas:
protected string $rol;
Encapsulamiento
El encapsulamiento consiste en proteger el estado interno de un objeto y controlar cómo se accede a él.
class Cuenta { private float $saldo = 0; public function depositar(float $importe): void { if ($importe <= 0) { return; } $this->saldo += $importe; } public function obtenerSaldo(): float { return $this->saldo; } }
El saldo no puede modificarse directamente desde afuera.
Getters y setters
Los getters permiten leer valores y los setters modificarlos de forma controlada.
class Producto { private float $precio; public function setPrecio(float $precio): void { if ($precio < 0) { throw new InvalidArgumentException( 'El precio no puede ser negativo' ); } $this->precio = $precio; } public function getPrecio(): float { return $this->precio; } }
Herencia
Una clase puede heredar propiedades y métodos de otra utilizando extends.
class Usuario { public function __construct( protected string $nombre ) { } public function getNombre(): string { return $this->nombre; } } class Administrador extends Usuario { public function eliminarUsuario(): string { return 'Usuario eliminado'; } }
Sobrescribir métodos
Una clase hija puede redefinir un método de la clase padre.
class Usuario { public function descripcion(): string { return 'Usuario del sistema'; } } class Administrador extends Usuario { public function descripcion(): string { return 'Administrador del sistema'; } }
parent::
Podemos llamar a un método de la clase padre utilizando parent::.
class Administrador extends Usuario { public function descripcion(): string { return parent::descripcion() . ' con permisos administrativos'; } }
Clases abstractas
Una clase abstracta sirve como base para otras clases y no puede instanciarse directamente.
abstract class Empleado { public function __construct( protected string $nombre ) { } abstract public function calcularSueldo(): float; }
Una clase hija debe implementar el método abstracto:
class EmpleadoMensual extends Empleado { public function __construct( string $nombre, private float $sueldo ) { parent::__construct($nombre); } public function calcularSueldo(): float { return $this->sueldo; } }
Interfaces
Una interfaz define qué métodos debe implementar una clase.
interface Notificable { public function enviar(string $mensaje): void; }
Después podemos crear distintas implementaciones:
class NotificacionEmail implements Notificable { public function enviar(string $mensaje): void { echo 'Enviando email: ' . $mensaje; } }
Polimorfismo
Gracias a una interfaz podemos trabajar con diferentes implementaciones utilizando el mismo contrato.
function notificar(
Notificable $servicio,
string $mensaje
): void {
$servicio->enviar($mensaje);
}
La función no necesita saber cómo se envía realmente la notificación.
Propiedades y métodos static
Un miembro static pertenece a la clase y no a una instancia concreta.
class Conversor { public static function kilometrosAMillas(float $km): float { return $km * 0.621371; } }
Se utiliza así:
$millas = Conversor::kilometrosAMillas(10);
Constantes de clase
class Pedido { public const ESTADO_PENDIENTE = 'pendiente'; public const ESTADO_PAGADO = 'pagado'; }
Podemos acceder mediante:
Pedido::ESTADO_PAGADO
Namespaces
Cuando un proyecto crece, los namespaces ayudan a organizar clases y evitar conflictos de nombres.
<?php namespace App\Models; class Usuario { } ?>
Luego podemos importar la clase:
use App\Models\Usuario; $usuario = new Usuario();
Ejemplo práctico: clase Producto
class Producto { public function __construct( private int $id, private string $nombre, private float $precio, private int $stock ) { } public function getNombre(): string { return $this->nombre; } public function getPrecio(): float { return $this->precio; } public function tieneStock(): bool { return $this->stock > 0; } public function descontarStock(int $cantidad): void { if ($cantidad <= 0 || $cantidad > $this->stock) { throw new InvalidArgumentException( 'Cantidad inválida' ); } $this->stock -= $cantidad; } }
Ventajas de la programación orientada a objetos
- organiza mejor proyectos grandes;
- permite reutilizar código;
- facilita separar responsabilidades;
- mejora el mantenimiento;
- permite crear componentes reemplazables;
- facilita las pruebas automatizadas;
- es la base conceptual de frameworks como Laravel.
No todo necesita ser una clase
La POO es una herramienta, no una obligación.
Crear clases sin una responsabilidad clara puede hacer que el código sea más complejo en lugar de mejorarlo.
Una buena clase debería representar una responsabilidad concreta y mantener relacionados sus datos y comportamientos.
Errores comunes
- hacer todas las propiedades públicas;
- crear clases que solamente agrupan funciones sin una responsabilidad clara;
- abusar de la herencia;
- crear jerarquías demasiado complejas;
- utilizar métodos
staticpara todo; - confundir una clase con un objeto;
- no utilizar tipos de datos cuando ayudan a detectar errores;
- crear clases gigantes que hacen demasiadas cosas.
Ejercicio práctico
Creá un pequeño sistema de biblioteca utilizando POO.
Definí:
- una clase
Librocon título, autor y disponibilidad; - una clase
Usuario; - un método para prestar un libro;
- un método para devolverlo;
- encapsulamiento para impedir cambios inválidos;
- una interfaz
Notificablepara informar cuando se realiza un préstamo.
¿Qué sigue?
En la próxima guía vamos a organizar un proyecto PHP utilizando una arquitectura más clara: MVC (Modelo, Vista y Controlador).
Veremos cómo separar:
- acceso a datos;
- lógica de negocio;
- controladores;
- vistas;
- rutas.
Esto nos va a acercar mucho más a la estructura que utilizan frameworks como Laravel.
¿Te sirvió esta guía? ☕
Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:
Descubre más desde Club Programador
Suscríbete y recibe las últimas entradas en tu correo electrónico.