Estándares PSR para escribir código en PHP

Aprende a escribir código en PHP que cumpla los estándares PSR

Image for post
Image for post

Históricamente PHP, a diferencia de otros lenguajes de programación, no ha tenido nunca un estándar que establezca la forma en que debe escribirse y estructurarse el código. Esto ha desembocado en problemas y confusiones como el famoso orden de los argumentos de las funciones explode e implode y que la convención para nombrar variables, el número de espacios empleados para tabulaciones o la definición de bloques de control if-else varíe de un desarrollador a otro.

Como sabréis, mantener un mismo estándar, además de reforzar el sentimiento de comunidad también ayuda a escribir código de mejor calidad y mantenible a largo plazo. Además, es uno de los pilares sobre los que debería cimentarse el trabajo en equipo, pues si todos los miembros adoptan la misma “guía de estilo” el tiempo de aprendizaje a la hora de trabajar con código ajeno será mucho menor.

Es por eso que me he animado a escribir este artículo recogiendo las 7 recomendaciones principales que toda la comunidad de desarrolladores PHP deberíamos seguir de cara a lograr un código mucho más legible e interoperable. Estas recomendaciones están extraídas de lo que se conoce dentro de la comunicad como PSR: “PHP Standards Recommendation”.

¡Espero que os sirvan de utilidad en proyectos futuros!

☝️ PSR-0. Autoloading Standard.

Esta primera recomendación define los requisitos obligatorios que aseguran la compatibilidad con el autoloader . Sin embargo, fue deprecada en favor de la recomendación titulada PSR-4.

  • La estructura de un “fully qualified namespace” y de una clase debe ser:
\<VendorName>\(<Namespace>\)*<Class Name>
  • Cada espacio de nombres (namespace) debe pertenecer a un espacio de nombres superior conocido como el “Vendor name”.
  • Cada espacio de nombres puede tener tantos subespacios como desee.
  • El separador dentro de un espacio de nombres es convertido en un DIRECTORY_SEPARATOR cuando se busca en el sistema de archivos.
  • El caracter _ en el nombre de la clases es convertido en un DIRECTORY_SEPARATOR .
  • El sufijo .php es añadido al “ fully qualified namespace” junto con la clase cuando se carga desde el sistema de archivos.
  • Se permite emplear para los nombres cualquier combinación de caracteres en mayúsculas o minúsculas.

Veamos algunos ejemplos:

\Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php

☝️ PSR-1. Basic Coding Standard.

PSR-1 define un estándar básico para escribir código.

Esta recomendación está centrada en definir convenciones para el nombrado y la estructura de archivos de cara asegurar un alto nivel de interoperabilidad entre códigos PHP escritos por distintos equipos o desarrolladores. Las convenciones que define son las siguientes

  • Emplear sólo las etiquetas <?php y <?= .
  • Emplear codificación UTF-8 sin BOM.
  • Separar declaraciones de los side-effects (¡hola wordpress!)
  • Asegurar el cumplimiento de la recomendación anterior
  • El nombre de la clase debe ser definido empleando la convención StudlyCaps.
  • Las constantes dentro de las clases deben ser definidas en mayúsculas empleando el caracter _ como separador.
  • El nombre de los métodos debe ser definido empleando la convención camelCase .
Image for post
Image for post
PSR-2 y PSR-12: la guía de estilo para código PHP

☝️ PSR-2 y PSR-12. Coding Style Guide.

PSR-2 extiende la recomendación PSR-1 define una serie de normas de estilo básico de cara a unificar el código escrito por distintos desarrolladores de modo que se reduzca la fricción al leer código ajeno. Actualmente se encuentra deprecada en favor de la recomendación PSR-12.

Por otra parte, la recomendación PSR-12 extiende y reemplaza a PSR-2 (la cual fue aceptada en 2010) de cara a actualizar las normas que en ella se sugieren de acuerdo a los cambios que ha habido en PHP y a aportar claridad sobre aquellas que pareciesen confusas.

Las normas propuestas en la especificación PSR-12 de cara a conseguir un estilo de código común a todos los desarrolladores PHP son las siguientes.

Archivos

  • El código debe seguir la recomendación PSR-1
  • Todos los archivos deben usar el fin de línea UNIX LF
  • Todos los archivos deben terminar con una línea en blanco
  • La etiqueta de cierre ?> debe ser omitida de archivos que sólo contengan PHP

Líneas

  • No hay un límite estricto para la longitud de las líneas pero de haberlo éste es de 120 caracteres. No obstante la recomendación es que las líneas no deberían tener más de 80 caracteres.
  • No debería haber espacios extras al final de las líneas que no estén en blanco
  • Se pueden añadir líneas en blanco para mejorar la legibilidad del código
  • No debe haber más de una instrucción por línea.

Tabulado

  • Se deben emplear 4 espacios para tabular y no usar el carácter de tabulación para este propósito.

Palabras clave de PHP

  • Las palabras clave de PHP deben estar escritas en minúscula al igual que las constantes true , false y null .
  • La forma corta de los tipos debe ser empleada en detrimento de la larga (por ejemplo, int en vez de integer ).

Declare, use y namespace

  • El orden en la cabecera de un archivo PHP es el siguiente: etiqueta de apertura <?php , docblock , sentencias declare , sentencia namespace , sentencias use para importar clases, sentencias use para importar funciones, sentencias use para importar constantes, comentario recordando el propósito del código del archivl.
  • Cada uno de los bloques anteriores deben ir separados por una línea en blanco y no deben contener líneas en blanco.
  • Sólo debe haber un use por línea.
  • Después de todas las declaraciones use debe haber una línea en blanco.
  • Los enunciados imports no deben comenzar nunca con el caracter `\` y deben estar siempre “fully qualified”.
<?phpdeclare(strict_types=1);namespace Acme;use \Vendor\Foo\{ClassA as A, Class B};
use \Vendor\Bar;
use function Vendor\Package\{functionA, functionB, functionC};use const Another\Vendor\CONSTANT_D;class MyClass
{
}

Clases

  • Cualquier llave de cierre no puede ser seguida de un comentario o sentencia en la misma línea.
  • Cuando se instancia una nueva clase los paréntesis deben estar siempre presentes aunque no se estén pasando argumentos al constructor.
  • La llave de apertura debe ir en su propia línea y no puede ser precedida o seguida de una línea en blanco.
  • La llave de cierre debe ir en la siguiente línea después del cuerpo de la clase y no puede ir precedida por una línea en blanco.

Extends e Implements

  • Las palabras clave extends e implements deben ser declaradas en la misma línea que el nombre de la clase.
class MyClass extends ParentClass implements MyInterface
{
// constants, properties, methods
}

Además, la lista de interfaces implementadas puede ser dividida en múltiplkes líneas, tabulando una vez cada línea así empleada. Si se emplea este estilo, el primer elemento de la lista debe aparecer en una nueva línea y sólo puede haber una interfaz por línea:

class MyClass extends ParentClass implements 
MyFirstInterface,
MySecondInterface
{
}

Traits

  • El uso de la palabra clave use para implementar traits debe aparecer en la siguiente línea después de la llave de apertura:
class MyClass
{
use MyTrait;
}
  • Cada trait debe ir en su propia línea y tener su propio use para ser importado.
  • Después de las sentencias use para implementar traits debe haber una línea en blanco salvo que la clase no contenga nada más.

Propiedades de clase

  • La visibilidad de cada propiedad y constante debe ser siempre declarada.
  • Cada propiedad debe ir en una línea.
  • La palabra clave var no puede ser usada para declarar una propiedad.
  • El nombre de las propiedades no puede llevar un guión bajo como prefijo de cara a indicar su visibilidad.

Métodos y funciones

  • La visibilidad debe ser declarada en todos los métodos.
  • La llave de apertura debe ir en su propia línea y la llave de cierre en la siguiente línea después del cuerpo del método o función.
  • No debe haber un espacio después del paréntesis de apertura y no debe haber un espacio antes del paréntesis de cierre.
  • No puede haber un espacio antes de cada coma.
  • Debe haber un espacio después de cada coma.
  • Los argumentos que lleven un valor por defecto deben ir al final de la lista de argumentos.
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// method body
}
  • En el caso de que sea necesario dividir los argumentos en diferentes líneas este es el estilo recomendado:
public function aLongMethod(
int $arg1,
string $arg2,
array $arg3 = []
) {
//method body
}
  • Cuando se especifique el tipo de retorno de la función, debe haber un espacio después de los dos puntos seguido del tipo. Tanto los dos puntos como el tipo deben estar en la misma línea que el paréntesis de cierra de la lista de argumentos sin espacio en blanco entre el paréntesis y los dos puntos.
class ReturnTypeVariations
{
public function functionName(int $arg1, $arg2): string
{
return 'foo';
}

public function anotherFunction(
string $foo,
string $bar,
int $baz
): string {
return 'foo';
}
}

Calificadores abstract, final y static.

  • En el caso de estar presentes, las declaraciones abstract y final deben ir antes de la declaración de visibilidad de la clase.
  • En el caso de estar presente, la declaración static debe ir después de la declaración de visibilidad.
abstract class MyClass
{
protected static $foo;
abstract protected function bar();
final public static function zeta()
{
}
}

Llamadas a funciones y métodos

  • No debe haber un espacio entre el nombre de la función y el paréntesis de apertura.
  • No debe haber un espacio después del paréntesis de apertura.
  • No debe haber un espacio antes del paréntesis de cierre.
  • En la lista de argumentos no debe haber un espacio antes de cada coma pero sí después de ella.
  • En el caso de que sea necesario separar en múltiples líneas la lista de argumentos éste es el estilo recomendado:
<?php$foo->bar(
$first,
$second,
$third
);
somefunction($foo, $bar, [
// ...
], $baz);

Estructuras de control de flujo

  • Debe haber un espacio después de las palabras if , else , for
  • No debe haber un espacio después del paréntesis de apertura.
  • No debe haber un espacio antes del paréntesis de cierre.
  • Debe haber un espacio entre el paréntesis de cierre y la llave de apertura.
  • El cuerpo de la estructura de control debe ir tabulada una vez.
  • El paréntesis de cierre debe ir en la siguiente línea.
  • La palabra clave elseif debería ser empleada en vez de else if .
  • else y elseif deben ir en la misma línea que la llave de cierre de la anterior estructura de control.
// Ejemplosif ($expr1) {
// if body
} elseif ($expr2) {
// elseif body
} else {
// else body;
}
if (
$expr1
&& $expr2
) {
// if body
} elseif (
$expr3
&& $expr4
) {
// elseif body
}
  • La palabra clave case debe ser tabulada una vez con respecto a switch .
  • La palabra clave break debe ser tabulada al mismo nivel que el cuerpo del case .
  • Debe haber un comentario cuando no se emplee la palabra break dentro de un case de cara a que se “caiga” en el siguiente caso.
// Ejemploswitch ($expr) {
case 0:
echo 'First case, with a break';
break;
case 1:
echo 'Second case, which falls through';
// no break
case 2:
case 3:
case 4:
echo 'Third case, return instead of break';
return;
default:
echo 'Default case';
break;
}
Image for post
Image for post

☝️ PSR-3. Logger Interface.

La recomendación PSR-3 define una interfaz común para todas las clases que implementen un servicio de logs de cara a mejorar su reusabilidad.

El objetivo de esta recomendación es permitir a las librerías recibir un objeto Psr\Log\LoggerInterface y escribir logs de una forma simple y universal.

La definición de esta interfaz la podéis encontrar en el siguiente enlace:

Al igual que la definición de los diferentes niveles que se pueden especificar a la hora de loggear un mensaje:

☝️ PSR-4. Improved Autoloading.

El objetivo de esta recomendación es sustituir a la PSR-0 de cara a abandonar el código anterior a la versión 5.3 y conseguir una estructura de carpetas más concisa.

Es decir, el objetivo de esta recomendación es unificar la forma en que las clases son compatibles con autoloading y describe dónde situar los archivos que vayan a ser “cargados de manera automática” de acuerdo a esta especificación.

Os dejo una tabla extraída de la página de la recomendación donde aparecen algunos ejemplos de las implicaciones de esta recomendación:

Image for post
Image for post
PSR-4
Image for post
Image for post
PSR-6 y PSR-16. Interfaces para librerías que implementen cachés

☝️ PSR-6 y PSR-16. Caching interfaces

PSR-6 tiene como objetivo definir una interfaz común a todas las librerías que implementen sistemas de cachés de modo que los desarrolladores no se vean obligados a aprender diferentes sistemas o a implementar adaptadores para integrarlas en sus proyectos.

PSR-16 es una recomendación complementaria a PSR-6 pero que presenta una aproximación algo más simple teniendo en cuenta los casos de uso más habituales. No obstante ambas recomendaciones son 100% compatibles.

PSR-6 en primer lugar define una serie de conceptos que deben ser tratados de la misma forma en todas las librerías que sigan esta recomendación: TTL, Expiration Date, el formato de las claves o el significado de un “miss” o un “hit”.

Además, establece que todas las librerías que sean desarrolladas de acuerdo a su especificación deben soportar todos los tipos PHP que son serializables y devolver el mismo tipo de datos que fue almacenado en primer lugar.

Finalmente define las interfaces CacheItemInterface que representa un elemento dentro de un sistema de caché, CacheItemPoolInterface que se encarga de aceptar una clave de la librería que emplea la caché y devolver el CacheItemInterface asociado y CacheException para ser usada en el momento en que surjan errores.

Por otra parte PSR-16 establece la interfaz CacheInterface que define las operaciones básicas sobre una colección de entradas en la caché: lectura, escritura y borrado de elementos individuales así como los métodos para trabajar con conjuntos de este tipo de elementos con el fin de realizar en una única llamada múltiples operaciones.

☝️ PSR-11. Container Interface

PSR-11 describe una interfaz común para todos los “dependency injection containers”.

Por tanto, el objetivo de ContainerInterface es estandarizar la forma en que los principales frameworks y librerías hacen un uso del container para obtener objetos y parámetros (a los que se denomina como entries). Básicamente lo que hace es exponer dos métodos:

  • get que debe recibir un string y devolver aquello que se encuentre en el container bajo esa clave
  • has que debe recibir un string y devolver true si el argumento es un identificador conocido dentro de ese container .

Además, es aquí donde se recomienda no pasar el container a los objetos de cara a que estos recuperen las dependencias.

Image for post
Image for post
PSR-14. Event Dispatcher

☝️ PSR-14. Event Dispatcher

El objetivo de PSR-14 es establecer un mecanismo común para extensiones basadas en eventos de modo que las librerías y componentes puedan ser reutilizados de forma más cómoda.

De este modo, PSR-14 establece la definición de Event , Listener , Dispatcher y ListenerProvider así como el funcionamiento de cada uno de estos elementos.

Además, en esta recomendación quedan establecidas las siguientes interfaces:

  • EventDispatcherInterface , es decir, el responsable de recuperar los Listeners de un Listener Provider para el evento que ha sido lanzado e invocar cada uno de esos Listeners .
  • ListenerProviderInterface , servicio responsable de determinar qué Listeners están interesados para un Event dado así como el orden en el que deberían ser ejecutados.

Conclusiones

Y hasta aquí las PSR que he considerado más interesantes y que pueden ayudarnos a escribir código PHP más estándar de cara a que otros desarrolladores puedan trabajar con él reduciendo la fricción que supone enfrentarse a código ajeno.

Espero que os hayan servido y que si echáis en falta alguna me la dejéis en los comentarios para añadirla.

¿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: 👇👇👇

Written by

Entre paseo y paseo con Simba desarrollo en Symfony y React