Mitigar el problema común de la inyección de dependencia en PHP

La inyección de dependencias es una técnica de software bien establecida que trata el problema de las dependencias de los objetos, lo que nos permite escribir clases poco acopladas. Si bien el patrón en sí ha existido durante bastante tiempo, el ecosistema PHP realmente no lo ha elegido hasta que los principales frameworks como Symfony comenzaron a implementarlo.

Hoy en día, es un estándar de facto para cualquier otra cosa que no sean tipos de aplicaciones triviales. Todo el problema de dependencia se observa fácilmente a través de un ejemplo simple:

<?php
class Customer
{
protected $name;
public function loadByEmail($email)
{
$mysqli = new mysqli('127.0.0.1', 'foggy', 'h4P9niq5', 'sakila');
$statement = $mysqli->prepare('SELECT * FROM customer WHERE email =
?');
$statement->bind_param('s', $email);
$statement->execute();
$customer = $statement->get_result()->fetch_object();
$this->name = $customer->first_name . ' ' . $customer->last_name;
return $this;
}
}
$customer = new Customer();
$customer->loadByEmail('MARY.SMITH@sakilacustomer.org');

Aquí, tenemos una clase Customer simple con un solo método loadByEmail(). La parte problemática es la dependencia del objeto $mysqli de la base de datos que está bloqueado en un método de instancia loadByEmail(). Esto permite un acoplamiento estrecho, lo que reduce la reutilización del código y abre la puerta a posibles efectos secundarios en todo el sistema inducidos por cambios de código posteriores. Para mitigar el problema, necesitamos inyectar el objeto de base de datos $mysqli en $customer.

La base de datos MySQL Sakila se puede obtener de https://dev.mysql.com/doc/sakila/en/.

Hay tres formas de inyectar la dependencia en un objeto:

  • Mediante un método de instancia
  • A través de un constructor de clase
  • A través de la propiedad de instancia

Mientras que el método de instancia y el enfoque de constructor de clase parecen un poco más populares que la inyección de propiedad de instancia.
El siguiente ejemplo demuestra el enfoque de usar un método de instancia para la inyección de dependencia:

<?php
class Customer
{
public function loadByEmail($email, $mysqli)
{
// …
}
}
$mysqli = new mysqli('127.0.0.1', 'foggy', 'h4P9niq5', 'sakila');
$customer = new Customer();
$customer->loadByEmail('MARY.SMITH@sakilacustomer.org', $mysqli);

Aquí, estamos inyectando una instancia del objeto $mysqli en una instancia del objeto Customer a través del método de instancia loadByEmail(). Si bien esto es ciertamente una mejor manera de crear instancias del objeto $mysqli dentro del método loadByEmail(), es fácil imaginar cuán rápido nuestro código podría volverse torpe si nuestra clase fuera a tener una docena de métodos, cada uno de los cuales requiere que se le pasen diferentes objetos. Si bien este enfoque puede parecer tentador, inyectar dependencias a través de métodos de instancia viola el principio de encapsulación de OOP. Además, agregar argumentos a los métodos en aras de la dependencia es cualquier cosa menos un ejemplo de mejores prácticas.
Otro enfoque sería utilizar el método del constructor de clases según el siguiente ejemplo:

<?php
class Customer
{
public function __construct($mysqli)
{
// …
}
public function loadByEmail($email)
{
// …
}
}

$mysqli = new mysqli('127.0.0.1', 'foggy', 'h4P9niq5', 'sakila');
$customer = new Customer($mysqli);
$customer->loadByEmail('MARY.SMITH@sakilacustomer.org');

Aquí, estamos inyectando una instancia del objeto $mysqli en una instancia del objeto Customer a través del método __constructor(). Ya sea una sola o una docena se inyectan objetos, la inyección del constructor sale como el claro ganador aquí. La aplicación cliente tiene un único punto de entrada para todas las inyecciones, lo que facilita el seguimiento de las cosas.
Sin la noción de inyección de dependencia, sería imposible lograr un código débilmente acoplado.

Comparte