PHP

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ícate

Control 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.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

20 % DE DESCUENTO

Plan mensual

19.00 /mes

15.20 € /mes

Precio normal mensual: 19 €
58 % DE DESCUENTO

Plan anual

10.00 /mes

8.00 € /mes

Ahorras 132 € al año
Precio normal anual: 120 €
Aprende PHP GRATIS online

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

PHP

Introducción Y Entorno

Instalación Y Primer Programa De Php

PHP

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

PHP

Sintaxis

Operadores Y Expresiones

PHP

Sintaxis

Estructuras De Control

PHP

Sintaxis

Funciones Y Llamada De Funciones

PHP

Sintaxis

Cadenas De Texto Y Manipulación

PHP

Sintaxis

Manejo De Números

PHP

Sintaxis

Manejo De Fechas Y Tiempo

PHP

Sintaxis

Manejo De Arrays

PHP

Sintaxis

Introducción A La Poo En Php

PHP

Programación Orientada A Objetos

Clases Y Objetos

PHP

Programación Orientada A Objetos

Constructores Y Destructores

PHP

Programación Orientada A Objetos

Herencia

PHP

Programación Orientada A Objetos

Encapsulación

PHP

Programación Orientada A Objetos

Polimorfismo

PHP

Programación Orientada A Objetos

Interfaces

PHP

Programación Orientada A Objetos

Traits

PHP

Programación Orientada A Objetos

Namespaces

PHP

Programación Orientada A Objetos

Autoloading De Clases

PHP

Programación Orientada A Objetos

Manejo De Errores Y Excepciones

PHP

Programación Orientada A Objetos

Manejo De Archivos

PHP

Programación Orientada A Objetos

Patrones De Diseño

PHP

Programación Orientada A Objetos

Introducción A Los Formularios En Php

PHP

Formularios

Procesamiento De Datos De Formularios

PHP

Formularios

Manejo De Archivos En Formularios

PHP

Formularios

Redirecciones Y Retroalimentación Al Usuario

PHP

Formularios

Formularios Dinámicos Y Separación De Lógica

PHP

Formularios

Introducción A La Persistencia En Php

PHP

Persistencia

Conexión A Bases De Datos

PHP

Persistencia

Consultas Y Operaciones Crud

PHP

Persistencia

Gestión De Transacciones

PHP

Persistencia

Manejo De Errores Y Excepciones En Base De Datos

PHP

Persistencia

Patrones De Acceso A Datos

PHP

Persistencia

Concepto De Sesiones En Php

PHP

Sesiones Y Cookies

Configuración De Sesiones

PHP

Sesiones Y Cookies

Cookies

PHP

Sesiones Y Cookies

Manejo Avanzado De Sesiones Y Cookies

PHP

Sesiones Y Cookies

Principales Vulnerabilidades En Php

PHP

Seguridad

Seguridad En Formularios Y Entrada De Datos

PHP

Seguridad

Protección Frente A Inyección Sql

PHP

Seguridad

Gestión De Contraseñas Y Cifrado

PHP

Seguridad

Seguridad En Sesiones Y Cookies

PHP

Seguridad

Configuraciones De Php Para Seguridad

PHP

Seguridad

Introducción Al Testing En Php

PHP

Testing

Phpunit

PHP

Testing

Cobertura De Código En Testing

PHP

Testing

Test Doubles (Mocks, Stubs, Fakes, Spies)

PHP

Testing

Pruebas De Integración Y Funcionales

PHP

Testing

Accede GRATIS a PHP y certifícate

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.