Introducción

El estilo de programación son las series de convenciones seguidas al desarrollar código fuente en un determinado lenguaje de programación.

Esta guía describe las convenciones usadas para el desarrollo en PHP aunque se pueden extender perfectamente a otros lenguajes con sintaxis similar.

El estilo de programación descrito en esta guía es sólo una recomendación, aunque se insta fervientemente a su utilización para garantizar que todo el código fuente generado por varias personas tenga un estilo similar con lo que se consigue que las revisiones de código entre los distintos programadores sea mucho más sencilla.

Estilo genérico usado

Dentro de los estilos más genéricos de programación, se usará el estilo BSD KNF (BSD Kernel Normal Form) y se parece mucho al estilo K&R.

Para más información véase la Wikipedia.

No se debería usar

No se deberían usar los siguientes estilos de programación:

Términos usados

Camel Case

Es la práctica de escribir frases o palabras compuestas eliminando los espacios y poniendo en mayúscula la primera letra de cada palabra.

Lower Camel Case

Similar al Camel Case sólo que la primera letra de la primera palabra es también en minúscula. Por ejemplo, la frase "generar datos aleatorios" pasada a lowerCamelCase sería "generarDatosAleatorios".

Upper Camel Case

Similar al Camel Case siendo la primera letra de la primera palabra en mayúscula. Por ejemplo, la frase "generar datos aleatorios" pasada a UpperCamelCase sería "GenerarDatosAleatorios".

Palabras reservadas

Las palabras reservadas del lenguaje así como las construcciones que se asimilan a funciones (array(), list(), etc.) se escribirán todas en minúsculas.

Indentación (sangrado)

La indentación o sangrado debe ser hecha mediante tabulaciones y no se deben insertar espacios. Se recomienda que el tamaño de las tabulaciones sea de 4 espacios aunque esto queda a opción del programador.

Variables

Las variables usadas en cualquier aplicación deben tener nombres que las identifiquen, es decir, nombres como "a", "b" o "c" no se deberían usar sino que deberían tener un nombre coherente con con lo que contienen. En el siguiente ejemplo los nombres de las variables son cortos por lo que no se identifica su significado:

$a = 0, $b = 0, $c = 0; if($a > 10 && $b > 20 && $c > 30) { return true; } else { return false; }

Este ejemplo debería ser:

$hora = 0, $minuto = 0, $segundo = 0; if($hora > 10 && $minuto > 20 && $segundo > 30) { return true; } else { return false; }

Sólo en el caso de que la variable sea lo demasiado genérica para no tener un nombre concreto (por ejemplo el índice de un bucle) ésta se declarará con el nombre de "i" siguiendo el orden alfabético en caso de varios bucles anidados ("i", "j", "k",...).

Los nombres largos de las variables se escribirán en lowerCamelCase.

Variables especiales

Existen variables que no son variables de tipo simple sino que son compuestas, por ejemplo, los arrays y las estructuras.

En PHP no existen estructuras ya que gracias a los arrays asociativos no son necesarias.

Para crear un array vacío, la sintaxis es la estándar:

$miArray = array();

Para crear un array monodimensional, la sintaxis sería la misma que la anterior:

$miArray = array(1,2,3,4,5);

Para crear un array multidimensional (o asociativo), éste se pondría en líneas separadas e indentadas una tabulación respecto al nombre de la variable que lo contiene de la siguiente forma:

$miArrayMultidimensional = array( 1 => "primero", 2 => "segundo", 3 => "tercero", 4 => "cuarto" );

En caso de ser un array asociativo, para mejorar la legibilidad, se debería indentar el valor de cada entrada a la misma altura de la siguiente forma:

$miArrayAsociativo = array( "Barcelona" => 08000, "Guadalajara" => 19000, "Leon" => 24000, "Madrid" => 28000, "Santa Cruz de Tenerife"=> 38000 ); $miArrayVariado = array( "primero" => 1, "segundo" => array( "hora" => 16, "minuto" => 58, "segundo" => 16 ), "tercero" => null );

En caso de que se puedan declarar estructuras del estilo de C, serían de la siguiente forma:

struct MiStruct { int valor1; int valor2; float valor3; char* nombre; };

Funciones

Los nombres de las funciones serán en lowerCamelCase. Los delimitadores del cuerpo de la función, en caso de que sean llaves ({ y }) estarán: el de apertura en la misma línea que la declaración de la función separado por un espacio (aunque se permite que esté en la siguiente línea). La llave de cierre debe estar en una línea a parte y no debe haber más código que la propia llave de cierre. Por ejemplo:

function getUserName($parametros) { // código }

En caso de que en las funciones se declare el tipo devuelto, este estará en una línea a parte encima del nombre de la función.

bool getUserName(void* parametros) { // código }

En caso de que haya muchos parámetros, estos estarán en múltiples líneas indentados con tabulaciones y espacios hasta coincidir en la misma columna que el primer parámetro. Por ejemplo:

function getDataFromDB($param1, $param2, $param3, $param4) { // código }

Los nombres de los parámetros, igual que los nombres de las variables, deben ser nombres concretos del tipo de parámetro en cuestión.

Dentro de la implementación de la función sólo debería haber un return aunque esta regla que se puede omitir siempre y cuando se mejore la legibilidad del código así como el rendimiento.

Clases e interfaces

Los nombres de las clases deben estar escritos en UpperCamelCase. La indentación de las mismas debe ser de una tabulación con respecto al margen izquierdo mientras que los campos y los métodos deben estar indentados una tabulación con respecto a la clase.

La implementación de los métodos deben estar indentadas con una tabulación respecto al nombre del método. El resto de estilos es similar al de las funciones normales.

Dentro de las clases, los métodos deben estar separados por un retorno de carro entre sí y por un retorno de carro tanto del inicio y final de la clase como de las propiedades.

Las propiedades similares pueden estar agrupadas (sin separar con retornos de carro) pero no en la misma línea, mientras que las propiedades que no tengan relación deben estar separadas por un retorno de carro igual que los métodos.

Si un método o propiedad tiene documentación, se aplica la misma normal de separación.

Tanto la clase como los métodos y propiedades deben estar documentados. Así mismo, en la clase deben existir los elementos @author y @version de la documentación aunque se pueden incluir más como @internal, @since, etc.

En caso de que un clase extienda otra o implemente una interfaz, la extensión debe estar en la misma línea que el nombre de la clase.

Los modificadores de clase o de método, como abstract, static, etc., también deben estar en la misma línea que el nombre que modifican.

Los nombres de las interfaces, por convención, deben comenzar por "I" mayúscula, por ejemplo: IConfig.

<?php /** * Documentación para la clase 'MiClaseDeEjemplo'. * * @author Diego Lago <beosman@gmail.com> * @version 0.1 2008-09-11 */ class MiClaseDeEjemplo extends MiClaseBase implements MiInterface { private static $campo1; private $campo2; static function miMetodo1() { } /** * Documentación para el método 'miMetodoDos'. * * @param mixed $param1 El primer parámetro. * @param mixed $param2 El segundo parámetro. * * @return mixed Valor de retorno. */ function miMetodoDos($param1,$param2) { } } ?>

Constantes

Las constantes dentro de una clase se deben declarar todas en mayúsculas y las diferentes palabras que la compongan separadas por barras bajas (_). Por ejemplo:

const ESTO_ES_UNA_CONSTANTE = 0;

Otra forma también permitida aunque no recomendada es que las constantes estén escritas en UpperCamelCase pero que siempre comiencen por la letra "k" minúscula. Por ejemplo:

const kEstoEsUnaConstante = 0;

En caso de que haya varias constantes dentro de la clase, las que estén relacionadas deben tener los valores a la misma altura. Por ejemplo:

const ENGINE_MSSQL = "mssql"; // Microsoft SQL const ENGINE_MYSQL = "mysql"; // MySQL const ENGINE_PGSQL = "pgsql"; // Postgre SQL const ENGINE_FIREBIRD = "firebird"; // Firebird const ENGINE_ORACLE = "oci"; // Oracle Call Interface const ENGINE_ODBC = "odbc"; // Open Database Connectivity const ENGINE_SQLITE = "sqlite"; // SQLite

Los comentarios de las constantes también deberían estar alineados verticalmente.

En el caso de que existen varias constantes que no estén relacionadas, estas deberían estar separadas por una línea en blanco.

Variables

Las variables de las clases, al igual que las variables normales, deben estar escritas en lowerCamelCase. Además, es recomendable declarar cada variable en una línea diferente junto con su modificador de acceso (public, private, protected). Por ejemplo:

public $primeraVariable; private $unaVariablePrivada; protected $variableProtegidaParaMisSubclases;

En caso de que existan varias variables relacionadas que requieran un valor de inicialización, éstas, al igual que las constantes, deben tener dichos valores alineados verticalmente.

Métodos

Los métodos de las clases, al igual que las funciones normales, deben estar escritos en lowerCamelCase. La forma de escribir los parámetros y el cuerpo es la misma que para las funciones normales.

Además, a las funciones hay que incorporarles su modificador de acceso aunque, en el caso de que sean públicas, no es necesario incluirlo. Por ejemplo:

private function getRealName() { // código }

Operadores

Los operadores, como +, +=, -, -=, *, *=, /, /=, %, %=, =, ==, ===, !=, >, >=, >>, <, <=, <<, =>, &, &&, |, ||, ?, etc., deben escribirse con espacios a ambos lados. Por ejemplo:

$miVariable = $otraVariable + 1 - ($pi * 3);

Una excepción a esta regla son los operadores unarios ++ y -- donde no es necesario (ni recomendable) poner un espacio antes (o después, según sea de postincremento o preincremento) de la variable que modifican. El siguiente ejemplo sería correcto:

$miVariableNumerica++;

Estas reglas de separación son útiles, por ejemplo, cuando existen varias expresiones que pueden dar lugar a ambigüedad:

$a = $b+++++$c;

La expresión anterior, si se siguen las directrices de separación de operadores, tendría la forma:

$a = $b++ + ++$c;

De esta forma el programador distingue perfectamente lo que hace la expresión anterior.

Expresiones booleanas

Cuando se comprueba una expresión y esta devuelve un valor booleano, es preferible hacerlo en una operación lógica booleana en lugar de introducirlo en una estructura de control de tipo if. Por ejemplo, en el siguiente código:

if($a > 100 && $b < 50 && $c != 4) { return true; } else { return false; }

Es más recomendable hacerlo de la siguiente forma:

return ($a > 100 && $b < 50 && $c != 4);

Esto es por mera sintaxis ya que el compilador genera el mismo código objeto para ambas expresiones.

Siempre que sea posible, se deberán utilizar valores booleanos en lugar de números, es decir, si una variable es booleana, no se le debe asignar "0" para false y "1" para true. Se le deben asignar las construcciones del lenguaje true y false.

Estructuras de control

Las estructuras de control son aquellas instrucciones del lenguaje que controlan el flujo de ejecución, es decir, sentencias que deciden qué ejecutar en cada caso, bucles de instrucciones, etc.

if

La sentencia if debe escribirse de la siguiente forma:

Un ejemplo de esta sentencia sería la siguiente:

if ($condicion) { // código } else if ($miVariable >= 100) { // código } elseif ($condicion || $miVariable < 100) { // código } else { // código }

switch

La sentencia switch debe escribirse de la siguiente forma:

Un ejemplo de un bloque switch sería el siguiente:

switch ($valor) { case 1: // código break; case 2: // código break; case 3: case 4: // código break; default: }

for

La sentencia for debe escribirse de la siguiente forma:

Un ejemplo de un bloque for sería el siguiente:

for ($i = 0; $i < 100; $i++) { // código }

foreach

La sentencia foreach sigue las mismas reglas que la sentencia for:

foreach ($array as $indice => $valor) { // código }

Sólo se debe poner el índice en el caso de que se vaya a usar.

while

La sentencia while se escribe de la siguiente forma:

Un ejemplo de escritura de la sentencia while sería el siguiente:

while ($condicion) { // código }

do while

La sentencia do while se escribe de la siguiente forma:

Un ejemplo de un bloque do while sería el siguiente:

do { // código } while ($condicion);

? (operador ternario)

El operador ternario es una forma simplificada de escribir una sentencia if. El operador ternario se debería escribir siempre en una misma línea y encerrado entre paréntesis (aunque no es estrictamente necesario). Hay que seguir las reglas de los operadores.

Tanto la condición como los bloques de código deben ser lo más pequeños posible ya que sino este operador estaría demasiado ofuscado. En caso de que sean demasiado grandes se debería usar una sentencia if.

Un ejemplo de operador ternario sería el siguiente:

return ($valor >= 5 ? "aprobado" : "suspenso");

Excepciones

Las excepciones son una forma de propagación de errores dentro de la aplicación. Las excepciones funcionan de forma similar a los objetos por lo que su guía de estilo no varía.

Para cada categoría de errores se debería crear un tipo nuevo de excepción que bastaría con que extienda una excepción ya conocida o la excepción base (class Exception).

Las excepciones se lanzan con el mismo nivel de indentado que la llamada a una función:

throw new MiExcepcion("Mensaje de la excepción");

try/catch

La forma de coger las excepciones antes de que se extiendan a toda la aplicación y la hagan parar es mediante la sentencia try/catch. Esta sentencia es un aglutinador de trozos de código que son susceptibles de lanzar alguna excepción y sirven para capturarla y evitar su propagación actuando en consecuencia.

La forma de capturar una excepción es la siguiente:

try { functionQueLanzaExcepcion(); } catch(Exception $e) { // tratamos la excepción aunque la podemos volver a // lanzar con un throw $e }

Dentro de la sentencia catch se puede poner cualquier tipo de excepción. Además, también pueden existir varios catch dentro del mismo bloque de código para capturar y tratar de forma distinta varios tipos de excepciones. Hay que recordar que el orden de captura debe ser el inverso al orden en el árbol de herencia de las excepciones.

La llave de apertura del try debe estar en la misma línea que el propio try. La llave de cierre del try (o de otro catch), el catch y la llave de apertura del catch deben estar en la misma línea. La llave de cierre del último catch debe estar en una línea diferente.

try/catch/finally

En caso de existir la sentencia finally, ésta tiene el mismo estilo que el catch:

try { functionQueLanzaExcepcion(); } catch(MyException $e) { // tratamos una excepción que hereda de Exception } catch(Exception $e) { // ahora tratamos la excepción base } finally { // este código siempre se ejecuta }

Comentarios

Los comentarios son la forma de aclarar el funcionamiento del código desarrollado así como generar la documentación específica de la API por lo que dichos comentarios son necesarios a lo largo de todo el desarrollo.

Existen dos tipos de comentarios: los comentarios de aclaración del código y los comentarios de documentación.

Comentarios de aclaración del código

Este tipo de comentarios se escribe en el medio del código para aclarar su funcionamiento. El comentario en cuestión se debe hacer en la línea inmediatamente superior a la línea de código fuente que se desea aclarar y se deben utilizar los comentarios de doble barra (//) de la siguiente forma:

// se comprueba por si la instancia es null if($instancia === null) { $error = "La instancia es NULL.; }

En caso de que la aclaración sea los suficientemente corta, se puede incluir en la misma línea de código. El punto y coma debe estar separado dos espacios de la primera barra invertida y el texto del comentario debe estar separado un espacio de la segunda barra:

$i++; // incrementamos la variable

En caso de que el comentario aclaratorio sea bastante largo, se deben usar los comentarios multilínea con apertura (/*) y cierre (*/). El uso de asteriscos en cada inicio de línea es opcional aunque recomendable teniendo en cuenta que muchos editores los añaden automáticamente:

/* * Este es un comentario multilínea para aclarar el * funcionamiento de una o varias líneas de código que * son difíciles de entender por la complejidad del * tratamiento de los datos. */ foreach($datos as $indice => $dato) { if(validarDato($dato)) { enviarDato($indice,$dato); } }

El nivel de indentación de los comentarios debe ser el mismo que el de la línea que comentan y no debería haber ninguna línea adicional entre la última línea del comentario y la línea de código fuente que comentan. Además, entre los asteriscos y el texto debe haber un espacio.

Comentarios de documentación

Los comentarios de documentación son comentarios multilínea con un formato especial y con palabras clave interpretadas por el generador de la documentación.

Estos comentarios se utilizan para la generación de la documentación del API del código fuente, tanto pública (hacia otros programadores) como privada (hacia los programadores de la aplicación).

Los comentarios de documentación comienzan por la apertura de un comentario multilínea seguido de un asterisco. Cada nueva línea del comentario debe ser precedida por un asterisco y su indentación debe ser la misma que el primer asterisco (no la barra oblícua) de la línea de apertura del comentario. El final de la línea debe estar indentada a la misma altura que las líneas intermedias.

Este tipo de comentario es igual que el comentario multilínea mencionado anteriormente añadiéndo un asterisco a la primera línea:

/** * Comentario de documentación de clases y funciones. * * Estos comentarios pueden llevar etiquetas especiales * con significado dentro de la documentación general: * * @author Diego Lago <beosman@gmail.com> * @version 0.2 2008-09-02 */

Los comentarios de documentación deben comentar las clases, los métodos y las funciones. Además, es recomendable comentar las variables de las clases.

Dentro de los comentarios de documentación, como se ha podido comprobar en el ejemplo anterior, pueden (y deben) existir etiquetas especiales que son interpretadas por el generador de documentación. Todas estas etiquetas comienzan por @ (arroba) y pueden ser las siguientes:

Cada una de estas etiquetas tiene una sintaxis específica por lo que se debe examinar la documentación del generador de documentación para ver su sintaxis específica.

Esta documentación, en caso de generarla, se genera en HTML por lo que se pueden incluir (y es recomendable) etiquetas HTML dentro de la propia documentación.

Conclusión

Estas reglas, como se comenta en el inicio de esta guía, son sólo opcionales aunque es muy recomendable su cumplimiento para que las distintas revisiones del código por distintas personas no sean demasiado complicadas y todos los integrantes del proyecto estén familiarizados con el mismo tipo de codificación.

Así mismo, en caso de seguirlas, estas reglas no son estrictas por lo que se puede relajar en la medida de lo posible para adaptarse a todos los estilos de programación de los distintos integrantes del grupo de trabajo.