PHP
Tutorial PHP: Encapsulación
PHP: Aprende a manejar el control de acceso a los miembros de una clase, aplicando encapsulación para proteger los datos y mejorar la seguridad.
Aprende PHP GRATIS y certifícateControl de acceso a miembros de la clase
En PHP, el control de acceso a los miembros de una clase es fundamental para garantizar la encapsulación y proteger la integridad de los datos. Permite definir la visibilidad y el alcance de las propiedades y métodos, estableciendo cuáles pueden ser accedidos desde fuera de la clase y cuáles son internos.
Existen tres niveles de visibilidad en PHP:
public: Los miembros declarados como public son accesibles desde cualquier lugar. Esto significa que pueden ser usados tanto dentro de la clase como desde instancias externas.
protected: Los miembros protegidos son accesibles desde la propia clase y desde las clases que heredan de ella. No pueden ser accedidos desde fuera de estas.
private: Los miembros privados solo son accesibles desde la clase en la que se declaran. Ni las clases hijas ni el código externo pueden acceder a ellos.
A continuación, se presenta un ejemplo que ilustra cómo funcionan estos niveles de acceso:
<?php
class Usuario
{
public $nombre;
protected $email;
private $password;
public function __construct($nombre, $email, $password)
{
$this->nombre = $nombre;
$this->email = $email;
$this->password = $password;
}
public function mostrarNombre()
{
echo "Nombre: " . $this->nombre . "\n";
}
protected function enviarEmail()
{
echo "Enviando email a " . $this->email . "\n";
}
private function encriptarPassword()
{
$this->password = md5($this->password);
}
public function procesar()
{
$this->encriptarPassword();
$this->enviarEmail();
}
}
class Administrador extends Usuario
{
public function mostrarInformacion()
{
echo "Nombre: " . $this->nombre . "\n";
// Accediendo a propiedad protegida desde la subclase
echo "Email: " . $this->email . "\n";
// Intento de acceso a propiedad privada (provocará un error)
// echo "Password: " . $this->password . "\n";
}
}
$usuario = new Usuario('Juan', 'juan@example.com', 'secreto');
$admin = new Administrador('Admin', 'admin@example.com', 'admin123');
// Acceso a propiedad pública
echo $usuario->nombre . "\n"; // Funciona
// Intento de acceso a propiedades protegidas o privadas (provocará errores)
// echo $usuario->email . "\n"; // Error
// echo $usuario->password . "\n"; // Error
// Llamada a método público
$usuario->mostrarNombre(); // Funciona
// Intento de llamada a método protegido o privado desde fuera de la clase
// $usuario->enviarEmail(); // Error
// $usuario->encriptarPassword(); // Error
// Uso de método público que internamente accede a miembros protegidos y privados
$usuario->procesar();
// Subclase accediendo a miembros protegidos
$admin->mostrarInformacion();
En este ejemplo:
La propiedad $nombre es pública y puede ser accedida desde cualquier lugar.
La propiedad $email es protegida y solo es accesible desde la clase Usuario y sus subclases.
La propiedad $password es privada y únicamente accesible desde la clase Usuario.
El método enviarEmail() es protegido y encriptarPassword() es privado. Ambos métodos no pueden ser llamados desde fuera de la clase, pero sí pueden ser utilizados internamente.
El control de acceso ayuda a mantener una abstracción adecuada y a evitar que partes externas del código interactúen directamente con datos sensibles. De esta manera, se asegura que las modificaciones o lecturas de determinados miembros se realicen a través de métodos controlados, aumentando la seguridad y la fiabilidad del programa.
Es recomendable utilizar el nivel de acceso más restrictivo que sea posible para cada miembro de la clase. Así se limita la exposición innecesaria de datos y se promueve una mejor práctica de programación orientada a objetos.
Getters y setters
En la programación orientada a objetos en PHP, los getters y setters son métodos que permiten acceder y modificar las propiedades de un objeto de manera controlada. Estos métodos son esenciales para mantener la encapsulación, ya que protegen los datos internos de la clase y proporcionan una interfaz pública para interactuar con ellos.
Al declarar una propiedad como privada o protegida, se impide su acceso directo desde fuera de la clase. Sin embargo, a través de un getter, podemos obtener su valor, y mediante un setter, podemos modificarlo aplicando cualquier lógica o validación necesaria.
Veamos un ejemplo con una clase Producto:
<?php
class Producto
{
private $nombre;
private $precio;
public function __construct($nombre, $precio)
{
$this->nombre = $nombre;
$this->setPrecio($precio);
}
// Getter para el nombre
public function getNombre()
{
return $this->nombre;
}
// Setter para el nombre
public function setNombre($nombre)
{
if (is_string($nombre) && !empty($nombre)) {
$this->nombre = $nombre;
} else {
echo "El nombre proporcionado no es válido.\n";
}
}
// Getter para el precio
public function getPrecio()
{
return $this->precio;
}
// Setter para el precio
public function setPrecio($precio)
{
if (is_numeric($precio) && $precio >= 0) {
$this->precio = $precio;
} else {
echo "El precio debe ser un número positivo.\n";
}
}
// Método para mostrar información del producto
public function mostrarInfo()
{
echo "Producto: " . $this->nombre . "\n";
echo "Precio: $" . number_format($this->precio, 2) . "\n";
}
}
$producto = new Producto("Laptop", 1200);
// Accedemos a propiedades mediante getters
echo "Nombre: " . $producto->getNombre() . "\n";
echo "Precio: $" . $producto->getPrecio() . "\n";
// Modificamos propiedades mediante setters
$producto->setNombre("Laptop Gamer");
$producto->setPrecio(1500);
// Mostramos la información actualizada
$producto->mostrarInfo();
// Intento de establecer un precio inválido
$producto->setPrecio(-300); // Mostrará un mensaje de error
// Intento de acceso directo a una propiedad privada (provocará un error)
// echo $producto->precio; // Error
En este ejemplo:
- Las propiedades $nombre y $precio son privadas, lo que significa que no pueden ser accedidas directamente desde fuera de la clase Producto.
- Los métodos getNombre() y setNombre() permiten obtener y establecer el valor de $nombre de forma controlada.
- En el setter de $precio, se incluyen validaciones para asegurarse de que el valor proporcionado es un número positivo. Si el valor no es válido, se muestra un mensaje de error, manteniendo así la integridad de los datos.
- Al intentar acceder directamente a la propiedad $precio, se produce un error, lo que demuestra la efectividad de la encapsulación.
El uso de getters y setters nos permite:
- Controlar cómo se accede y modifica una propiedad.
- Implementar validaciones y lógica adicional al establecer o recuperar valores.
- Mantener las propiedades internas del objeto protegidas de accesos indebidos.
- Proporcionar una interfaz estable, incluso si la implementación interna cambia.
Es posible también utilizar propiedades de solo lectura o solo escritura. Por ejemplo, si queremos que una propiedad sea inmutable una vez establecida, podemos no definir un setter para ella:
class Orden
{
private $numeroOrden;
private $fecha;
public function __construct($numeroOrden)
{
$this->numeroOrden = $numeroOrden;
$this->fecha = date("Y-m-d");
}
// Solo getter para $numeroOrden
public function getNumeroOrden()
{
return $this->numeroOrden;
}
// Solo getter para $fecha
public function getFecha()
{
return $this->fecha;
}
}
$orden = new Orden(1001);
echo "Número de Orden: " . $orden->getNumeroOrden() . "\n";
echo "Fecha: " . $orden->getFecha() . "\n";
// Intento de modificar $numeroOrden (no hay setter disponible)
// $orden->setNumeroOrden(1002); // Error: Método no existente
En este caso:
- Las propiedades $numeroOrden y $fecha solo tienen getters, por lo que, una vez asignadas en el constructor, no pueden ser modificadas desde fuera de la clase.
- Esto es útil cuando se desea que ciertos datos permanezcan constantes durante la vida del objeto.
Implementar getters y setters es una práctica recomendada que mejora la modularidad y mantenibilidad del código. Permite que los objetos controlen su propio estado y que los cambios internos no afecten a las partes del programa que interactúan con ellos a través de su interfaz pública.
Finalmente, es importante mencionar que, a partir de PHP 7.4, se introducen las propiedades tipadas, lo que permite declarar el tipo de datos que una propiedad puede almacenar, proporcionando mayor robustez al código. Sin embargo, incluso con propiedades tipadas, el uso de getters y setters sigue siendo esencial para aplicar lógica adicional y mantener una correcta encapsulación.
Métodos mágicos __get() y __set()
En PHP, los métodos mágicos __get() y __set() permiten interceptar las operaciones de lectura y escritura de las propiedades de un objeto. Estos métodos se invocan automáticamente cuando se intenta acceder o asignar un valor a una propiedad inaccesible o inexistente, proporcionando así una forma flexible de manejar propiedades dinámicas o controlar el acceso a los datos del objeto.
El método __get($nombrePropiedad) se ejecuta cuando se intenta leer una propiedad que no es accesible desde el contexto actual. Por su parte, el método __set($nombrePropiedad, $valor) se invoca al intentar asignar un valor a una propiedad inaccesible. Estas situaciones ocurren cuando las propiedades son privadas, protegidas o simplemente no existen en la clase.
Veamos un ejemplo práctico:
<?php
class Persona
{
private $datos = [];
public function __get($nombre)
{
if (array_key_exists($nombre, $this->datos)) {
return $this->datos[$nombre];
} else {
echo "Propiedad '$nombre' no definida.\n";
return null;
}
}
public function __set($nombre, $valor)
{
echo "Estableciendo '$nombre' a '$valor'.\n";
$this->datos[$nombre] = $valor;
}
}
$persona = new Persona();
$persona->nombre = "Carlos";
$persona->edad = 30;
echo $persona->nombre . "\n"; // Carlos
echo $persona->edad . "\n"; // 30
echo $persona->correo . "\n"; // Propiedad 'correo' no definida.
En este ejemplo:
- Se define un array $datos privado para almacenar los valores de las propiedades dinámicas.
- Al asignar $persona->nombre = "Carlos";, se invoca automáticamente el método __set(), que almacena el valor en $datos.
- Cuando se accede a $persona->nombre, se llama al método __get(), que recupera el valor desde $datos.
- Si se intenta acceder a una propiedad no establecida previamente, como $persona->correo, el método __get() maneja la situación adecuadamente.
El uso de los métodos mágicos __get() y __set() permite:
- Encapsular el acceso a las propiedades, proporcionando un control adicional.
- Implementar propiedades dinámicas, donde los nombres de las propiedades se determinan en tiempo de ejecución.
- Integrar lógicas de validación o transformación al acceder o asignar valores.
Es importante tener en cuenta que abusar de estos métodos puede dificultar la legibilidad y el mantenimiento del código. Por ello, se recomienda utilizarlos con moderación y ser explícito en el manejo de propiedades cuando sea posible.
Otro ejemplo, integrando validaciones:
<?php
class Producto
{
private $atributos = [];
public function __set($nombre, $valor)
{
if ($nombre === 'precio') {
if (is_numeric($valor) && $valor >= 0) {
$this->atributos[$nombre] = $valor;
} else {
echo "El precio debe ser un número positivo.\n";
}
} else {
$this->atributos[$nombre] = $valor;
}
}
public function __get($nombre)
{
if (array_key_exists($nombre, $this->atributos)) {
return $this->atributos[$nombre];
} else {
echo "Propiedad '$nombre' no definida.\n";
return null;
}
}
}
$producto = new Producto();
$producto->nombre = "Smartphone";
$producto->precio = 250;
echo $producto->nombre . "\n"; // Smartphone
echo "Precio: $" . $producto->precio . "\n"; // Precio: $250
$producto->precio = -50; // El precio debe ser un número positivo.
En este caso:
- Se implementa una validación en __set() para la propiedad precio, asegurando que solo se asignen valores numéricos positivos.
- Los mensajes de error son manejados dentro de los métodos mágicos, mejorando la robustez de la clase.
- La propiedad nombre se asigna sin restricciones adicionales.
También es posible combinar los métodos mágicos con una gestión de propiedades más estricta. Por ejemplo, utilizando un array de propiedades permitidas:
<?php
class Usuario
{
private $propiedadesPermitidas = ['nombre', 'email'];
private $datos = [];
public function __set($nombre, $valor)
{
if (in_array($nombre, $this->propiedadesPermitidas)) {
$this->datos[$nombre] = $valor;
} else {
echo "No se puede establecer la propiedad '$nombre'.\n";
}
}
public function __get($nombre)
{
if (in_array($nombre, $this->propiedadesPermitidas)) {
return $this->datos[$nombre] ?? null;
} else {
echo "No se puede acceder a la propiedad '$nombre'.\n";
return null;
}
}
}
$usuario = new Usuario();
$usuario->nombre = "Laura";
$usuario->email = "laura@example.com";
$usuario->edad = 25; // No se puede establecer la propiedad 'edad'.
echo $usuario->nombre . "\n"; // Laura
echo $usuario->edad . "\n"; // No se puede acceder a la propiedad 'edad'.
Aquí, se controla qué propiedades pueden ser establecidas o accedidas, manteniendo la integridad de la clase.
Es crucial recordar que los métodos __get() y __set() solo se activan cuando se intenta acceder o modificar propiedades inaccesibles. Si la propiedad es pública y existe, estos métodos no se invocan. Por ejemplo:
<?php
class Ejemplo
{
public $propiedad = "Valor inicial";
public function __get($nombre)
{
echo "Intentando acceder a '$nombre'.\n";
return null;
}
}
$e = new Ejemplo();
echo $e->propiedad . "\n"; // Valor inicial
echo $e->otraPropiedad . "\n"; // Intentando acceder a 'otraPropiedad'.
En este caso:
- Al acceder a $e->propiedad, no se llama a __get(), ya que la propiedad es pública y existe.
- Al acceder a $e->otraPropiedad, se invoca __get(), porque la propiedad no está definida en la clase.
Los métodos mágicos son herramientas poderosas que, utilizadas correctamente, pueden mejorar la flexibilidad y funcionalidad de nuestras clases en PHP. Sin embargo, es fundamental usarlos con cautela y mantener un equilibrio entre flexibilidad y claridad en el código.
Todas las lecciones de PHP
Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Php
Introducción Y Entorno
Instalación Y Primer Programa De Php
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Manejo De Números
Sintaxis
Manejo De Fechas Y Tiempo
Sintaxis
Manejo De Arrays
Sintaxis
Introducción A La Poo En Php
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Traits
Programación Orientada A Objetos
Namespaces
Programación Orientada A Objetos
Autoloading De Clases
Programación Orientada A Objetos
Manejo De Errores Y Excepciones
Programación Orientada A Objetos
Manejo De Archivos
Programación Orientada A Objetos
Patrones De Diseño
Programación Orientada A Objetos
Introducción A Los Formularios En Php
Formularios
Procesamiento De Datos De Formularios
Formularios
Manejo De Archivos En Formularios
Formularios
Redirecciones Y Retroalimentación Al Usuario
Formularios
Formularios Dinámicos Y Separación De Lógica
Formularios
Introducción A La Persistencia En Php
Persistencia
Conexión A Bases De Datos
Persistencia
Consultas Y Operaciones Crud
Persistencia
Gestión De Transacciones
Persistencia
Manejo De Errores Y Excepciones En Base De Datos
Persistencia
Patrones De Acceso A Datos
Persistencia
Concepto De Sesiones En Php
Sesiones Y Cookies
Configuración De Sesiones
Sesiones Y Cookies
Cookies
Sesiones Y Cookies
Manejo Avanzado De Sesiones Y Cookies
Sesiones Y Cookies
Principales Vulnerabilidades En Php
Seguridad
Seguridad En Formularios Y Entrada De Datos
Seguridad
Protección Frente A Inyección Sql
Seguridad
Gestión De Contraseñas Y Cifrado
Seguridad
Seguridad En Sesiones Y Cookies
Seguridad
Configuraciones De Php Para Seguridad
Seguridad
Introducción Al Testing En Php
Testing
Phpunit
Testing
Cobertura De Código En Testing
Testing
Test Doubles (Mocks, Stubs, Fakes, Spies)
Testing
Pruebas De Integración Y Funcionales
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Entender la importancia de la encapsulación en PHP.
- Identificar y aplicar los niveles de visibilidad: public, protected y private.
- Implementar getters y setters para el acceso controlado a propiedades.
- Utilizar métodos mágicos para manejar propiedades dinámicas.
- Reconocer las prácticas recomendadas para mantener la integridad de los datos.