Qué es y para qué sirve PHP Mess Detector

PHPMessDetector es una herramienta que ayuda a detectar código defectuoso en proyectos PHP (Muy útil a la hora de realizar auditorías de código ajeno).

Se basa en el análisis automatizado del código utilizando conjuntos de reglas.

Estas reglas buscan detectar código mal estructurado, mala nomenclatura, métodos exageradamente grandes, ciclos ineficientes y demás.

Está preparado para emitir su salida en diferentes formatos (Texto, XML, HTML).

Su uso es bastante simple, hay que instalarlo y luego corre como una utilidad de línea de comandos.

Por ejemplo, corriendo el comando

php phpmd.phar . text naming

Sobre una base de código algo dudoso, obtuve un resultado como este:

/home/mauro/sitio/authorize/authorizenet.php:11 Classes should not have a constructor method with the same name as the class/home/mauro/sitio/authorize/authorizenet.php:11 Classes should not have a constructor method with the same name as the class/home/mauro/sitio/authorize/authorizenet.php:42 Avoid variables with short names like $ch. Configured minimum length is 3./home/mauro/sitio/authorize/authorizenet.php:87 Avoid variables with short names like $j. Configured minimum length is 3.

Otro análisis interesante es este:

php phpmd.phar . text codesize

Que me da este resultado (Entre otros):

/home/mauro/sitio/phpminiadmin.php:157	The function display_select() has an NPath complexity of 26112. The configured NPath complexity threshold is 200.

Si vemos lo que hay a partir de la línea 157:

function display_select($sth,$q){
 global $dbh,$DB,$sqldr,$reccount,$is_sht,$xurl;
 $rc=array("o","e");
 $dbn=$DB['db'];
 $sqldr='';

 $is_shd=(preg_match('/^show\s+databases/i',$q));
 $is_sht=(preg_match('/^show\s+tables|^SHOW\s+TABLE\s+STATUS/',$q));
 $is_show_crt=(preg_match('/^show\s+create\s+table/i',$q));

 if ($sth===FALSE or $sth===TRUE) return;#check if $sth is not a mysql resource

 $reccount=mysql_num_rows($sth);
 $fields_num=mysql_num_fields($sth);

 $w='';
 if ($is_sht || $is_shd) {$w='wa';
   $url='?'.$xurl."&db=$dbn";
   $sqldr.="<div class='dot'>
&nbsp;MySQL Server:
&nbsp;&#183;<a href='$url&q=show+variables'>Show Configuration Variables</a>
&nbsp;&#183;<a href='$url&q=show+status'>Show Statistics</a>
&nbsp;&#183;<a href='$url&q=show+processlist'>Show Processlist</a>
<br>";
   if ($is_sht) $sqldr.="&nbsp;Database:&nbsp;&#183;<a href='$url&q=show+table+status'>Show Table Status</a>";
   $sqldr.="</div>";
 }
 if ($is_sht){
   $abtn="&nbsp;<input type='submit' value='Export' onclick=\"sht('exp')\">
 <input type='submit' value='Drop' onclick=\"if(ays()){sht('drop')}else{return false}\">
 <input type='submit' value='Truncate' onclick=\"if(ays()){sht('trunc')}else{return false}\">
 <input type='submit' value='Optimize' onclick=\"sht('opt')\">
 <b>selected tables</b>";
   $sqldr.=$abtn."<input type='hidden' name='dosht' value=''>";
 }

 $sqldr.="<table class='res $w'>";
 $headers="<tr class='h'>";
 if ($is_sht) $headers.="<td><input type='checkbox' name='cball' value='' onclick='chkall(this)'></td>";
 for($i=0;$i<$fields_num;$i++){
    if ($is_sht && $i>0) break;
    $meta=mysql_fetch_field($sth,$i);
    $headers.="<th>".$meta->name."</th>";
 }
 if ($is_shd) $headers.="<th>show create database</th><th>show table status</th><th>show triggers</th>";
 if ($is_sht) $headers.="<th>engine</th><th>~rows</th><th>data size</th><th>index size</th><th>show create table</th><th>explain</th><th>indexes</th><th>export</th><th>drop</th><th>truncate</th><th>optimize</th><th>repair</th>";
 $headers.="</tr>\n";
 $sqldr.=$headers;
 $swapper=false;
 while($row=mysql_fetch_row($sth)){
   $sqldr.="<tr class='".$rc[$swp=!$swp]."' onmouseover='tmv(this)' onmouseout='tmo(this)' onclick='tc(this)'>";
   for($i=0;$i<$fields_num;$i++){
      $v=$row[$i];$more='';
      if ($is_sht && $v){
         if ($i>0) break;
         $vq='`'.$v.'`';
         $url='?'.$xurl."&db=$dbn";
         $v="<input type='checkbox' name='cb[]' value=\"$vq\"></td>"
         ."<td><a href=\"$url&q=select+*+from+$vq\">$v</a></td>"
         ."<td>".$row[1]."</td>"
         ."<td align='right'>".$row[4]."</td>"
         ."<td align='right'>".$row[6]."</td>"
         ."<td align='right'>".$row[8]."</td>"
         ."<td>&#183;<a href=\"$url&q=show+create+table+$vq\">sct</a></td>"
         ."<td>&#183;<a href=\"$url&q=explain+$vq\">exp</a></td>"
         ."<td>&#183;<a href=\"$url&q=show+index+from+$vq\">ind</a></td>"
         ."<td>&#183;<a href=\"$url&shex=1&t=$vq\">export</a></td>"
         ."<td>&#183;<a href=\"$url&q=drop+table+$vq\" onclick='return ays()'>dr</a></td>"
         ."<td>&#183;<a href=\"$url&q=truncate+table+$vq\" onclick='return ays()'>tr</a></td>"
         ."<td>&#183;<a href=\"$url&q=optimize+table+$vq\" onclick='return ays()'>opt</a></td>"
         ."<td>&#183;<a href=\"$url&q=repair+table+$vq\" onclick='return ays()'>rpr</a>";
      }elseif ($is_shd && $i==0 && $v){
         $url='?'.$xurl."&db=$v";
         $v="<a href=\"$url&q=SHOW+TABLE+STATUS\">$v</a></td>"
         ."<td><a href=\"$url&q=show+create+database+`$v`\">sct</a></td>"
         ."<td><a href=\"$url&q=show+table+status\">status</a></td>"
         ."<td><a href=\"$url&q=show+triggers\">trig</a></td>"
         ;
      }else{
       if (is_null($v)) $v="NULL";
       $v=htmlspecialchars($v);
      }
      if ($is_show_crt) $v="<pre>$v</pre>";
      $sqldr.="<td>$v".(!strlen($v)?"<br>":'')."</td>";
   }
   $sqldr.="</tr>\n";
 }
 $sqldr.="</table>\n".$abtn;

}

Podemos ver un código algo complicado… aún si no sabemos calcular el NPathComplexity :).

Si bien las reglas que trae incorporada la herramienta suelen dar buenos indicios, no debe tomarse todo como verdad incuestionable.

Particularmente, hay una regla que, al menos a mi, me genera cierta desconfianza: ElseExpression (Del conjunto CleanCode).

Según su definición, una sentencia else nunca es necesaria y sólo hace al código más difícil de leer… probablemente tenga razón en la primera parte (De que no es 100% necesaria, ahora, si hace más difícil de leer el código… no me parece tan simple de afirmar).

De cualquier forma, es interesante contar con una herramienta que pueda orientar nuestros esfuerzos rápidamente hacia las partes más controvertidas del código.

Una característica muy interesante de PHPMD es que te permite definir tus propias reglas de análisis (Yo por ejemplo usaría una que detecte el uso de variables globales).

Si bien es evidente que sólo con el análisis estático del código no es posible dar un diagnóstico completo, ciertamente ayuda contar con este tipo de herramientas.

¿Alguna vez te tocó analizar código de terceros? ¿Cómo te las arreglaste?

mchojrin

Por mchojrin

Ayudo a desarrolladores PHP a acceder mercados y clientes más sofisticados y exigentes

¿Te quedó alguna duda? Publica aca tu pregunta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.