Pasar al contenido principal

Asegurando la calidad de Software: analizadores estáticos de código

Durante el desarrollo de código (módulos, librerías, themes) es importante integrar herramientas objetivas que midan el estado del código y te provean de información para medir la calidad de tu código y de esta forma poder detectar y prevenir problemas derivados de código de baja calidad: funciones duplicadas, métodos excesivamente complejos, estilo de codificación completamente diferente por cada desarrollador… Idealmente debemos incorporar estas herramientas al comienzo del desarrollo pero si aún no lo has hecho debes incorporarlos cuanto antes, anotar los reportes iniciales y trabajar para mejorar las cifras de problemas detectados. Es recomendable automatizar estas pruebas en tu sistema de Integración continua para que se ejecuten en cada nueva versión del software (pull al repositorio) y puedas contar con un histórico.

Librerías

Existen muchas herramientas para mejorar/auditar la calidad del código, entre las herramientas que utilizamos diariamente en Metadrop destacamos:

PHPCS / Drupal Coder

Extensión sobre PHP_CodeSniffer (phpcs) para detectar código que no sigue los estándares de Drupal tanto a nivel de sintaxis como buenas prácticas de código, así como fallos de seguridad a la hora de construir consultas a la base de datos. Cuando todos los desarrolladores utilizan el mismo estilo de codificación se favorece un código más uniforme y facilita la revisión de código.

https://packagist.org/packages/drupal/coder

phpcs-hist.png

PHPCS results

PHPMD​

Extensión PHP para detección de diferentes problemas como código excesivamente complejo (complejidad ciclomática) de forma que podamos detectar fácilmente puntos conflictivos que pueden suponer focos de errores así como dificultad de actualización en un futuro.

https://packagist.org/packages/phpmd/phpmd

phpmd-hist.png

PHP Resultados ejemplo

PHPCPD​

Extensión PHP para detección de código duplicado. Es frecuente encontrar en proyectos con plazos ajustados que no se abstrae el código en funciones de forma eficiente incluso algoritmos/procedimientos exactamente iguales en módulos independientes fruto de la desconexión entre los miembros del equipo de trabajo. Este punto es fundamental detectarlo y solucionarlo ya que va a provocar inconsistencias y un esfuerzo adicional a la hora de actualizar/refactorizar cualquier componente.

https://packagist.org/packages/tonicforhealth/phpcs

phpcpd-hist.png

PHP copy paste resultados ejemplo

PHPLOC

Extensión PHP para medir rápidamente el tamaño y analizar la estructura de un proyecto PHP.

https://packagist.org/phploc/phploc

Integración con Drupal

La integración con Drupal así como cualquier otro software PHP es sencilla. En nuestro caso la estructura de directorios del proyecto es la siguiente:

docroot/ <- código de drupal
build.xml <- fichero integración phing
logs_codereview/.. <- resultados de tests
...

Es decir, tenemos un directorio del proyecto del que cuelga un directorio docroot con el código de Drupal, un fichero build.xml con la configuración de integración de phing, y un directorio logs_codereview donde se escribirán los resultados de los análisis. 

PHING

Para la automatización de los procesos utilizamos PHING, si conoces ANT es el mismo concepto pero para PHP.

Phing es una herramienta de compilación o construcción de proyectos basada en Apache Ant. Puede hacer todo lo posible con un sistema de compilación tradicional como GNU make y su uso de archivos de compilación XML sencillos y clases extensibles de PHP "task" hace que sea un marco de compilación fácil de usar y altamente flexible.

Puedes ver un listado de todas las herramientas integradas en phing en https://www.phing.info/docs/guide/trunk/ y recuerda que siempre puedes extender phing o ejecutar una instrucción concreta con <exec> por lo que siempre tendrás libertad de acción.

Instalación de librerías sobre PHING

Phing puede ser instalado con composer fácilmente:

# Creamos directorio, por ejemplo en OPT:
mkdir /opt/phing && cd /opt/phing
 
# Instalamos librerías (composer):
composer require phing/phing
composer require sebastian/phpcpd
composer require phpmd/phpmd
composer require pdepend/pdepend
composer require phploc/phploc
composer require phpunit/phpunit
composer require phpunit/php-code-coverage
composer require drupal/coder
 
# Symlink en /usr/bin
ln -s /opt/phing/vendor/bin/phing /usr/local/bin/phing
 
# Comprobamos version:
phing -version
 
# Añadimos el coding standar de Drupal a phpCS
phpcs --config-set installed_paths /opt/phing/vendor/drupal/coder/coder_sniffer

Build XML

En el fichero build.xml definimos los ficheros a auditar, en este caso están todos dentro del directorio docroot (el buil.xml en el directorio padre) así como todos procesos (targets) que se van a ejecutar.

<?xml version="1.0"?>
<project name="code-review" default="phpcs">

   <!- Ficheros a auditar ----->
  <fileset dir="docroot" id="targetfiles">
		
    <include name="sites/all/modules/custom/**/*.php" />
    <include name="sites/all/modules/custom/**/*.module" />
    <include name="sites/all/modules/custom/**/*.inc" />
    <include name="sites/all/modules/custom/**/*.js" />
    <include name="sites/all/modules/custom/**/*.info" />

    <include name="sites/all/themes/custom/**/*.tpl" />
    <include name="sites/all/themes/custom/**/*.theme" />
    <include name="sites/all/themes/custom/**/*.info" />
                
    <exclude name="sites/all/modules/custom/md_ckeditor/plugins/ckeditor/youtube/**/*.js" />
    <exclude name="sites/all/modules/custom/md_ckeditor/plugins/ckeditor/archery_link/plugin.js" />
  </fileset>

  <!- PHPCS ----->
  <target name="phpcs" description="Run phpcs">

    <phpcodesniffer standard="Drupal" format="checkstyle" allowedFileExtensions="php,inc,module,install,profile">
      <fileset refid="targetfiles" />
      <formatter type="checkstyle" outfile="reports/codereview/checkstyle-result.xml" />
      <config name="installed_paths" value="/opt/phing/vendor/drupal/coder/coder_sniffer"/>
    </phpcodesniffer>

  </target>

  <!- PHPMD ----->
  <target name="phpmd" description="Run phpmd">

    <phpmd>
      <fileset refid="targetfiles" />
      <formatter type="xml" outfile="reports/codereview/pmd.xml" />
    </phpmd>

  </target>

  <!- PHPCPC ----->
  <target name="phpcpd" description="Run phpcpd">
    <phpcpd>
      <fileset refid="targetfiles" />
      <formatter type="pmd" outfile="reports/codereview/cpd.xml"/>
    </phpcpd>
  </target>

  <!- PHPLOC ----->
  <target name="phploc" description="Measures the size of the project and count it's tests">
    <phploc countTests="true" reportType="txt" reportName="loc" reportDirectory="logs_codereview">
      <fileset refid="targetfiles" />
    </phploc>
  </target>

</project>

Ejecución de PHING

En la ejecución de phing le indicamos el build.xml que ha de procesar y los targets que queremos ejecutar:

phing -f build.xml phpcs phpmc phpcpc phploc

La salida de phing

En nuestro ejemplo la salida la hemos volcado al directorio logs_codereview/ que será posteriormente analizada por el sistema de integración continua, en nuestro caso Jenkins.

$ ls -l logs_codereview/

checkstyle-result.xml
cpd.xml
loc.txt
pmd.xml

Conclusiones

A través de phing la integración de distintas herramientas de análisis estático de código es una tarea sencilla, fácil de implementar y altamente recomendable para dar un paso más en el objetivo de asegurar la calidad del código.

¿Utilizas otras herramientas de para analizar código? ¡Compártelas en los comentarios!

 

Jorge Tutor

Jorge Tutor

CIO