PHP. 8 trucos que te ayudarán a escribir código más limpio
Recopilación de algunos trucos que podemos usar en PHP para escribir mejor código
English version: https://medium.com/@ger86/php-8-tricks-that-will-help-you-write-cleaner-code-374c71daffb6
Hoy he querido crear un artículo con una recopilación de trucos que nos ayudarán a escribir código PHP más limpio. He querido preparar un artículo de este tipo ya que muchas veces el día a día nos obliga a ir mucho más rápido de lo que deberíamos provocando que no podamos profundizar en el lenguaje o estar al tanto de las últimas novedades que van apareciendo en cada versión.
Así que vamos a verlas. Espero que os resulten útiles para vuestros proyectos.
Nota. Emplearé el nombre en inglés de aquellos operadores o conceptos que no tengan una traducción al español popularizada. Así mantenemos un mismo vocabulario :)
1. Null coalescing operator
Para mí este operador es uno de los más útiles. Su aparición tuvo lugar con la salida de PHP 7.0 y nos va a permitir ahorrarnos un poco de código en este tipo de comprobaciones:
$someArray = ['one' => 'foo', 'two' => 'bar'];$one = isset($someArray['one'] ? $someArray['one'] : 'empty';
// 'foo'$three = isset($someArray['three'] ? $someArray['three] : 'empty';
// 'empty'
Ya que nos va a permitir simplificarlas del siguiente modo:
$someArray = ['one' => 'foo', 'two' => 'bar'];$one = $someArray['one'] ?? 'empty';
// 'foo'$three = $someArray['three] ?? 'empty';
// 'empty'
Es decir, gracias al operador ??
(llamado “null coalescing operator”) podremos ahorrarnos la comprobación isset
cada vez que queramos comprobar si una clave se encuentra en un array. Muy muy útil y sobre todo más limpio.
2. Invokable classes
Este es uno de los conceptos que a menudo son desconocidos a la hora de trabajar con clases en PHP y que sin embargo están comenzando a popularizarse gracias a frameworks como Symfony que los emplean de forma recurrente en determinadas situaciones.
¿Qué es una “invokable class”? Pues como su propio nombre indica es una clase que podemos invocar como si de una función se tratara.
Esto es posible gracias al método mágico __invoke
de PHP que podemos declarar dentro de nuestra clase:
class A {
public function __invoke() {
print_r('Hello world!);
}
}$a = new A();
($a)(); // 'Hello World'class B {
public function __invoke(int $a): int {
return $a * 2;
}
}$b = new B();
($b)(5); // 10
Como veis, al añadir el método invoke
dentro de nuestra clase podemos emplear una instancia de la misma como si de una función se tratara. Para ello basta con rodearla entre paréntesis y ya PHP lo interpretará por nosotros para llamar al método __invoke
del objeto.
¿Su utilidad? Por ejemplo para definir Event Listeners
o Handlers
de mensajes que tan sólo necesitan tener implementado el método público encargado de procesar el evento o el mensaje. Gracias a la posibilidad de declarar clases invocables no es necesario llamar a un método de la clase en concreto.
3. Traits
Sobre los “traits” de PHP ya preparé un artículo en su momento en el que explicaba lo que son y la forma de emplearlos para crear código reusable entre nuestras clases:
Sin embargo, sí he creído conveniente mencionarlos en este artículo por la posibilidad que nos dan a la hora de agrupar código común de distintas clases y aislarlo en un componente.
Por ejemplo, supongamos que tenemos unas cuantas clases que tienen la propiedad createdAt
:
// A.php<?php class A { private $createdAt; public function getCreatedAt(): \DateTimeInterface {
return $this->createdAt;
} public function setCreatedAt(\DateTimeInterface $createdAt): self {
$this->createdAt = $createdAt;
return $this;
} // getters and setters and rest of properties
}// B.phpclass B { private $createdAt; public function getCreatedAt(): \DateTimeInterface {
return $this->createdAt;
} public function setCreatedAt(\DateTimeInterface $createdAt): self {
$this->createdAt = $createdAt;
return $this;
} // getters and setters and rest of properties
}
Este código al ser común a ellas podemos agruparlo en un Trait
de modo que cada vez que queramos implementar dicha funcionalidad no sea necesario copiar y pegar:
// Timestampable.phptrait Timestampable { private $createdAt; public function getCreatedAt(): \DateTimeInterface {
return $this->createdAt;
} public function setCreatedAt(\DateTimeInterface $createdAt): self {
$this->createdAt = $createdAt;
return $this;
}}// A.php<?phpclass A { use Timestampable; // getters and setters and rest of properties
}// B.phpclass B { use Timestampable; // getters and setters and rest of properties
}
De este modo, tendremos agrupada la funcionalidad pudiendo acceder a la variable createdAt
como si hubiera sido declarada de la forma “normal” dentro de nuestra clase.
$a = new A();
$a->getCreatedAt();
4. array_map, array_filter, array_reduce
Ahora que está tan de moda emplear el estilo de programación declarativo (especialmente en Javascript) no está de más recordar la posibilidad que tenemos de emplear estas funciones para iterar sobre los arrays.
array_map
nos permite iterar sobre cada uno de los elementos de un array para operar sobre ellos y generar un nuevo array:
// imperative $myArray = [1, 2, 3];
$multipliedArray = [];
foreach ($myArray as $item) {
$multipliedArray[] = $item * 2;
}
// declarative$multipliedArray = array_map(
function(int $item) { return $item * 2; },
$myArray
);
array_filter
nos permite filtrar los elementos de un array en base a una condición:
// imperative$myArray = [1, 2, 3];
$filteredArray = [];
foreach ($myArray as $item) {
if ($item % 2 === 0) {
$filteredArray[] = $item;
}
}// declarative$filteredArray = array_filter(
$myArray,
function(int $item) { return $item % 2 === 0; }
);
array_reduce
nos permite reducir un array a un valor acumulando en una variable el resultado. Por ejemplo, sirve para sumar los elementos de un array:
// imperative$myArray = [1, 2, 3];
$total = 0;
foreach ($myArray as $item) {
$total += $item;
}// declarative$total = array_reduce(
$myArray,
function(int $ac, int $item) { return $ac + $item; },
0
);
5. Desestructurar arrays
Esto es una de las características que más me gustan de PHP (seguramente porque estoy muy acostumbrado a Javascript) y probablemente más desconocidas: la posibilidad de extraer elementos de un array sin necesidad de acceder a ellos mediante su clave o posición.
Por ejemplo:
$people = [
[1, 'Tom'],
[2, 'Fred'],
];
// Estilo de []
[$firstPerson, $secondPerson] = $people;[$id, $name] = $firstPerson;
Esto también podemos emplearlo dentro de bucles foreach
como por ejemplo:
foreach ($people as [$id, $name]) {
// do something
}
Lo cual nos provee de una manera muy limpia de acceder a los arrays sin tener que recurrir de forma explícita a su posición.
6. Arrow functions
Enlazando con el uso de las funciones array_map
, array_reduce
y array_filter
con la llegada de PHP 7.4 (que está al caer: 28 de noviembre de 2019) tendremos una importante novedad: poder emplear “arrow functions”.
De este modo se refuerza el estilo de programación funcional a la vez que aligeramos un poco el código de determinadas expresiones:
$numbers = [1, 2, 3];
$multipliedNumbers = array_map(fn($x) => $x * 2, $numbers);
De este modo, podemos pasar a escribir funciones de una única línea cuando necesitemos declarar funciones anónimas o callables haciendo mucho más sencillo de leer el código.
Creo que es en este tipo de cosas en donde se nota el esfuerzo que está haciendo el equipo que mantiene PHP en actualizar el lenguaje de cara a evolucionar su sintaxis y darnos mejores herramientas para desarrollar. ¿Está PHP muerto? No, no lo creo.
7. Invocar métodos y funciones mediante variables
Otro posible gran desconocido y que sin embargo nos puede aportar gran versatilidad a nuestro código: la posibilidad de invocar una función o método mediante una variable que contenta un string
con el nombre de la función o método a invocar.
Por ejemplo:
class A {
public function someMethod() {
echo 'hello world';
}
}$a = new A();
$method = 'someMethod';
$a->$someMethod(); // hello world
Esto suele ser de utilidad para definir por ejemplo callbacks de modo que podamos decir a la función que llame al callback qué método de nuestro objeto emplear.
class Invoker {
public function executeMethod($object, string $method) {
$object->$method();
}
}class A {
public function doSomething() {
echo 'hello world';
}
}$a = new A();
$invoker = new Invoker();
$invoker->executeMethod($a, 'doSomething');
8. Finally en try/catch
La instrucción finally
nos permite ejecutar código al final de una declaración try/catch
de modo que podemos llevar a cabo una tarea independientemente del flujo de ejecución que se haya seguido.
Por ejemplo, podemos emplearlo para cerrar un archivo mediante fclose
una vez que hayamos terminado de escribir en él o si por alguna circunstancia se produjo una excepción durante la ejecución del código situado dentro de try
:
$file = fopen('log.txt', 'a');
try {
fwrite($file, 'Throwing exception..');
throw new \Exception();
} catch (\Exception $e) {
echo 'Threw a RangeException: ' . $e->getMessage();
} finally {
// Always make sure that we close the file
echo 'Reached the finally block';
fwrite($file, 'Reached the finally block');
fclose($file);
}
Conclusiones
Espero que estos ocho consejos para escribir código más limpio en PHP os hayan parecido interesantes y que os sirvan para seguir mejorando como desarrolladores.
También os dejo un par de artículos en donde hablo de las novedades de PHP 7.3 y PHP 7.4 por si queréis seguir al día de las nuevas herramientas de las que disponemos:
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇