Curso de PHP
Encapsulamiento en PHP: public, private y protected
Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de PHP, hoy veremos el encapsulamiento: el pilar de la POO que protege los datos de tus objetos.
¡Empecemos!
Qué es el encapsulamiento
El encapsulamiento consiste en ocultar el estado interno de un objeto y permitir el acceso solo a través de métodos controlados.
La analogía de siempre: un cajero automático. Usas los botones y la pantalla, no metes la mano en la máquina para sacar el dinero. La máquina decide qué operaciones permite y bajo qué condiciones.
El problema sin encapsulamiento
Mira esta clase con todo público:
<?php
class CuentaBancaria
{
public float $saldo = 0;
}
$cuenta = new CuentaBancaria();
$cuenta->saldo = -50000; // nada lo impide
Un saldo negativo de cincuenta mil. La clase no puede evitarlo porque cualquiera puede escribir directamente en la propiedad.
Y el problema no es solo el valor absurdo: es que si mañana aparece un saldo incorrecto, el culpable puede estar en cualquiera de los cien archivos que tocan esa propiedad.
Los tres modificadores de acceso
| Modificador | Desde la clase | Desde una hija | Desde fuera |
|---|---|---|---|
public |
Sí | Sí | Sí |
protected |
Sí | Sí | No |
private |
Sí | No | No |
<?php
class Ejemplo
{
public string $publica = "accesible desde todos lados";
protected string $protegida = "solo la clase y sus hijas";
private string $privada = "solo esta clase";
}
$obj = new Ejemplo();
echo $obj->publica; // funciona
echo $obj->protegida; // Error
echo $obj->privada; // Error
Se aplican tanto a propiedades como a métodos.
La regla práctica
Empieza haciendo todo privado y abre solo lo necesario.
Es mucho más fácil hacer pública una propiedad que hacía falta que quitar el acceso a una que ya está siendo usada en veinte sitios.
Getters y setters
Si las propiedades son privadas, se accede a ellas mediante métodos:
<?php
declare(strict_types=1);
class CuentaBancaria
{
private float $saldo = 0;
public function __construct(
private readonly string $titular,
) {}
public function getTitular(): string
{
return $this->titular;
}
public function getSaldo(): float
{
return $this->saldo;
}
public function depositar(float $monto): void
{
if ($monto <= 0) {
throw new InvalidArgumentException("El monto debe ser positivo");
}
$this->saldo += $monto;
}
public function retirar(float $monto): void
{
if ($monto <= 0) {
throw new InvalidArgumentException("El monto debe ser positivo");
}
if ($monto > $this->saldo) {
throw new RuntimeException("Saldo insuficiente");
}
$this->saldo -= $monto;
}
}
Ahora es imposible dejar la cuenta en un estado inválido. No existe forma de tocar $saldo sin pasar por las validaciones.
<?php
$cuenta = new CuentaBancaria("Víctor Peña");
$cuenta->depositar(1000);
$cuenta->retirar(300);
echo $cuenta->getSaldo(); // 700
// $cuenta->saldo = 99999; // Error
No todo necesita getter y setter
Aquí hay un malentendido muy extendido. Mucha gente aprende encapsulamiento y escribe esto:
<?php
class Producto
{
private string $nombre;
public function getNombre(): string
{
return $this->nombre;
}
public function setNombre(string $nombre): void
{
$this->nombre = $nombre;
}
}
Eso no es encapsulamiento: es una propiedad pública con dos métodos de por medio. Cualquiera puede leer y escribir sin ninguna restricción, igual que antes, pero con el triple de código.
El encapsulamiento real aparece cuando el método hace algo:
- Valida (
depositarrechaza montos negativos). - Calcula (
getSalarioTotal()suma base y comisiones). - Mantiene la coherencia (al cambiar el estado de un pedido, registra la fecha).
Si un setter solo asigna, plantéate si la propiedad no debería ser pública, o readonly si no debe cambiar.
Métodos privados
El encapsulamiento también aplica al comportamiento. Un método privado es un paso interno que nadie de fuera necesita conocer:
<?php
class Pedido
{
private array $lineas = [];
private float $descuento = 0;
public function calcularTotal(): float
{
$subtotal = $this->calcularSubtotal();
$conDescuento = $this->aplicarDescuento($subtotal);
return $this->aplicarImpuesto($conDescuento);
}
private function calcularSubtotal(): float
{
return array_reduce(
$this->lineas,
fn($total, $linea) => $total + ($linea['precio'] * $linea['cantidad']),
0
);
}
private function aplicarDescuento(float $monto): float
{
return $monto - ($monto * $this->descuento);
}
private function aplicarImpuesto(float $monto): float
{
return round($monto * 1.13, 2);
}
}
Desde fuera solo existe calcularTotal(). Los tres pasos internos están ocultos, y eso significa que puedes cambiarlos cuando quieras sin romper a nadie: nadie los está llamando.
Esa es la ventaja menos evidente y más valiosa del encapsulamiento. Lo público es un compromiso; lo privado es libertad para cambiar.
Encapsulamiento con readonly
Desde PHP 8.1, cuando un dato debe leerse pero nunca cambiar, readonly es más directo que un getter:
<?php
class Factura
{
public function __construct(
public readonly string $numero,
public readonly DateTimeImmutable $fecha,
private array $lineas = [],
) {}
}
$f = new Factura("F-001", new DateTimeImmutable());
echo $f->numero; // se lee sin problema
// $f->numero = "F-002"; // Error
Menos código y la misma protección.
protected: para las clases hijas
protected es el punto intermedio, y su uso natural es la herencia:
<?php
abstract class Empleado
{
public function __construct(
protected string $nombre,
protected float $salarioBase,
) {}
protected function calcularBonos(): float
{
return 0;
}
public function calcularSalario(): float
{
return $this->salarioBase + $this->calcularBonos();
}
}
class Vendedor extends Empleado
{
public function __construct(
string $nombre,
float $salarioBase,
private float $ventas,
) {
parent::__construct($nombre, $salarioBase);
}
protected function calcularBonos(): float
{
return $this->ventas * 0.05; // accede a lo protegido
}
}
calcularBonos() es un detalle interno que las hijas necesitan redefinir, pero que nadie debe llamar desde fuera.
Un aviso sobre las constantes de clase
Las constantes también admiten modificadores:
<?php
class Pedido
{
public const ESTADO_PAGADO = 'pagado';
private const CLAVE_INTERNA = 'xyz';
}
Errores comunes
- Poner todo público «por comodidad». El sistema queda sin defensas.
- Generar getters y setters automáticamente para cada propiedad. Es encapsulamiento aparente.
- Usar
privatedonde hacía faltaprotected. Las clases hijas no van a poder acceder. - Devolver arrays u objetos internos desde un getter: quien lo reciba puede modificarlos y estás filtrando el estado interno.
Para cerrar
El encapsulamiento no va de escribir getters: va de que un objeto nunca pueda quedar en un estado inválido. Si tus métodos públicos validan y mantienen la coherencia, el objeto se protege solo.
Y recuerda la libertad que da: todo lo privado puedes reescribirlo mañana sin avisar a nadie.
En la siguiente lección veremos las clases abstractas.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp