Blog de Israel Viana

Artículos de Abril de 2009

Error garrafal en Tuenti

20 de abril de 2009 | 2 comentarios

Si hace unas semanas ponía un pantallazo de un buscador de torrent como muestra de lo que no se debe hacer en producción, ahora una web conocida por todos, Tuenti, comete un garrafal error con uno de sus servidores de pruebas (accesible públicamente):

Error garrafal en Tuenti

Con cosas como ésta y el hecho de que tres años después todas las imágenes subidas por los usuarios sigan siendo públicas (puedes probar tú mismo a acceder a la URL de una foto con la sesión cerrada) demuestran la incompetencia absoluta y el verdadero interés de quienes están detrás de Tuenti, hacer negocio. Aunque de eso ya hablaremos con calma en breve...


Relacionando enlaces en Delicious y contenidos en el blog

4 de abril de 2009 | 0 comentarios
Enlazando enlaces en Delicious y contenidos en el blog

Hoy vamos a ver una aplicación experimental de eso que llaman "linked data", una tendencia que podría considerarse web semántica, aunque sin ontologías ni estándares, sino basada en RSS, servicios web y API específicas para cada servicio: se trata de obtener enlaces en Delicious relacionados con los contenidos del blog.

Lo que hace este sencillo algoritmo es comparar las "nubes" de tags: obtiene los últimos enlaces de una cuenta de Delicious, comprueba que contengan las tags más recurrentes en el blog, y, cuanto más concurrentes sean las tags en el blog, más puntuación de afinidad se le da al enlace.

El ejemplo en acción lo puedes ver ahí a la derecha. Las tags más recurrentes del blog (web semántica, web social, evolución de internet...) están presentes en los enlaces.

El algoritmo puede ser útil para saber cómo evolucionan los intereses de redacción y navegación del blogger o aportar contenido añadido al blog. Estoy preparando más artículos con más criterios para evaluar la "cercanía temática" de los artículos, basándose en las etiquetas.

El código de abajo funciona sobre el gestor de contenidos de este sitio. Si a alguien le interesa hacer un plug-in para WordPress que soporte esta funcionalidad lo podemos intentar. Habría que adaptar la obtención de los datos (adaptando las sentencias SQL a la BD de WordPress o sustituyéndolas por llamadas a la API) y el modo de mostrarlos (sin Smarty).

Función para ordenar los enlaces

//Ordena una matriz por el valor de una de sus claves (primer nivel)
//TODO parece que no funciona bien
function array_sort_clave(&$data, $campo) {
  //Crea una función personalizada para el campo
  $codigo_funcion = "return strcmp(\$a['$campo'], \$b['$campo']);";
  usort($data, create_function('$a,$b', $codigo_funcion));
  return $data;
}

Clase Enlace (la utilizo para los enlaces de Delicious y para el blogroll)

class Enlace {
  public $titulo;
  public $url;
  public $icono;
  public $rel;
  public $tags = array();
  
  public function __construct($titulo, $url, $icono, $rel=null, $tags=null) {
    $this->titulo = $titulo;
    $this->url = $url;
    $this->icono = $icono;
    $this->rel = $rel;
    $this->tags = $tags;
  }
}

La "chicha"

//Obtiene una nube de tags correspondiente a todos los post
static public function getTagcloudSimple() {
  $tagcloud = array();
  $cTags = BDConsulta("SELECT tag FROM tags WHERE post IN (SELECT id FROM posts ORDER BY creado DESC)");
  while ($tag = recorrer_resultados($cTags)) {
    $tagcloud[$tag['tag']]++;
  }
  return $tagcloud;
}

//Obtiene enlaces de Delicious, en orden de afinidad con el blog
//La afinidad con el blog se calcula a partir de las tags de los posts del blog. A cada enlace se le asigna un índice de afinidad
//El índice de afinidad se calcula sumando las veces que cada tag del enlace aparece en el blog
static public function getDelicious() {
  $enlaces = self::getEnlacesDelicious();
  $enlaces_con_puntos = array();
  $tags_blog = self::getTagcloudSimple();
  foreach ($enlaces as $enlace) {
    $puntos = 0;
    foreach ($enlace->tags as $tag_del_enlace) {
      $puntos += $tags_blog[$tag_del_enlace];
    }
    $enlaces_con_puntos[] = array('datos'=>$enlace, 'puntos'=>$puntos);
  }
  array_sort_clave($enlaces_con_puntos, "puntos");
  $enlaces_final = array();
  $cnt = count($enlaces_con_puntos);
  //Ordena inversamente la matriz, aunque ordena sólamente las claves
  for ($i=0; $i<$cnt; $i++) {
    $enlaces_final[$cnt-1-$i] = $enlaces_con_puntos[$i];
  }
  //Ordena el array según las nuevas claves. Vaya lío...
  ksort($enlaces_final);
  return $enlaces_final;
}
  
//Se procesa el RSS de los enlaces y se crea un array de objetos Enlace con él
static public function getEnlacesDelicious($limite=30) {
  if (!is_int($limite)) trigger_error("El parámetro debe ser un entero", E_USER_ERROR);
  //$xml = simplexml_load_file(RUTA_L . "delicious.xml");
  $xml = simplexml_load_file("http://feeds.delicious.com/v2/rss/isra00?count=$limite");
  $enlaces = array();
  foreach ($xml->channel->item as $item) {
    $tags_item = array();
    //No vale pasar $item->category directamente al constructor de Enlace, mejor pasarlo a array de cadenas
    foreach ($item->category as $s) {
      $tags_item[] = utf8_decode((string) $s);
    }
    $enlaces[] = new Enlace(utf8_decode($item->title), utf8_decode($item->link), null, null, $tags_item);
  }
  
  return $enlaces;
}

Mostramos los enlaces en una plantilla Smarty

<h3>Enlaces Delicious</h3> <div id="blogroll" class="sb_seccion pq">
    <p>Mostrando enlaces relacionados con el blog. <a href="http://delicious.com/isra00/">Ver todos los enlaces</a>.</p>
    <ul>
    {foreach from=$delicious item=enlace name=i}{if $smarty.foreach.i.index < 10}
        <li>
            <a href="{$enlace.datos->url}">{$enlace.datos->titulo}</a>
            {foreach from=$enlace.datos->tags item=tag}
            <a class="insignificante" href="http://delicious.com/isra00/{$tag}">[{$tag}]</a>
            {/foreach}
            </li>
    {/if}{/foreach}
    </ul>
</div>

Plantillas PHP: cuestión de rendimiento

2 de abril de 2009 | 5 comentarios

He leído dos posts bastante viejos (2006 y 2007) del gran Ricardo Galli sobre el rendimiento y escalabilidad de Twitter y los sistemas de plantillas en PHP. En ambos posts las discusiones han sido muy ricas, y me llevan a dos grandes conclusiones: aunque el cache y escalabilidad horizontal son soluciones importantes para la alta disponibilidad de sitios web, es vital escribir buen código. Y en ese sentido, los sistemas de plantillas y los frameworks pueden jugar malas pasadas.

Plantillas PHP, sin pseudo-lenguajes

En posts recientes he hablado de Smarty y de PHPTAL, dos sistemas de plantillas que añaden un lenguaje nuevo a nuestras aplicaciones web, ya cargadas con PHP, SQL, XHTML, CSS, JavaScript y nosecuantas siglas más. Eso significa que, además de la curva de aprendizaje, hay que cargar el proyecto con un parser del pseudo-lenguaje. En el caso de Smarty, dicen ser rápidos, aunque hay un fork del proyecto llamado TemplateLight que dice ser más liviano que su "papá".

Existe un método para utilizar plantillas y no usar sistemas adicionales. Es una sistema sencillo, estúpidamente sencillo, pero eficiente y prácticamente con los mismos beneficios que usar Smarty u otros: plantillas en PHP, embebiendo los códigos en los documentos HTML, pero sin las chapuzas de siempre, separando las capas de lógica y vista.

Podemos crear una pequeña biblioteca de funciones para gestionar plantillas de este tipo y que los nombre de variables no causen conflictos. Voy a hacer algún proyecto con este sistema, a modo de prueba (es lo que tiene ser freelance, puedo usar la tecnología que me dé la gana ;-). Utilizar plantillas en PHP permite separar la presentación de la lógica, pero sin la bajada de rendimiento de los sistemas que parsean plantillas escritas en un pseudo-lenguaje como Smarty, del que ya se ha hablado por aquí. Reconozco que las plantillas PHP no tienen la legibilidad de Smarty o PHPTAL, pero se le acerca bastante.

simpletpl.php (la librería del motor)


<?php

//Directorio donde están las plantillas
define("TPL_DIR", "tpl");

//Array que alojará las variables de las plantillas
$tpl_vars = array();

//Asigna una variable a la plantilla
function tpl_asignar($nombre, $valor) {
    global $tpl_vars;
    $tpl_vars[$nombre] = $valor;
}

//Devuelve una variable de la plantilla
function tpl($variable) {
    global $tpl_vars;
    if (isset($tpl_vars[$variable])) return $tpl_vars[$variable];
    else trigger_error("La variable '$variable' no existe", E_USER_ERROR);
}

//Carga la plantilla especificada
function tpl_cargar($plantilla) {
    global $tpl_vars;
    if (file_exists(TPL_DIR)) {
        if (file_exists(TPL_DIR . "/$plantilla")) {
            include(TPL_DIR . "/$plantilla");
        } else trigger_error("La plantilla '$plantilla' no existe", E_USER_ERROR);
    } else trigger_error("El directorio de plantillas especificado no existe. Verifique la constante TPL_DIR", E_USER_ERROR);
}

//Indenta un texto al $n - ésimo nivel
function tab($texto, $n) {
    return preg_replace('/\n/', "\n" . str_repeat("\t", $n), $texto);
}

?>

inicio.tpl.php (un ejemplo de plantilla cualquiera)


<html>
<head>
    <title><?php echo tpl('titulo') ?></title>
</head>
<body>
    <h1><?php echo tpl('titulo') ?></h1>
    
<?php foreach (tpl('posts') as $p) { ?>
    <h2><a href="<?php echo $p['url'] ?>"><?php echo $p['titulo'] ?></a></h2>
    <div style="color: gray"><?php echo $p['creado'] ?></div>

    <?php echo tab($p['cuerpo'], 2) ?>

<?php } ?>

<?php tpl_cargar("pie.tpl.php") ?>

pie.tpl.php (otra plantilla de ejemplo, para ser incluida)

<div style="background: silver; padding: 10px">2008 © Israel Viana</div>
</body>
</html>

index.php (script que obtiene los datos y carga la plantilla)


<?php
include("simpletpl.php");
$conexion = mysql_connect("localhost", "root", "root");
mysql_select_db("blog");

$q = mysql_query("SELECT * FROM posts ORDER BY id DESC LIMIT 5");

$posts = array();
while ($p = mysql_fetch_assoc($q)) {
    $p['url'] = "http://www.israelviana.es/post.php?id=" . $p['id'];
    $posts[] = $p;
}

tpl_asignar("titulo", "Blog de Israel Viana");
tpl_asignar("posts", $posts);

tpl_cargar("inicio.tpl.php");

?>

FAIL

1 de abril de 2009 | 0 comentarios

Error en PHP

Buscando un torrent para descargar la última de James Bond he encontrado el pedazo de error que estás viendo. El sitio web, basado en PHP, no se puede conectar al servidor de base de datos por cualquier motivo, y los errores se suceden. Pero el gran fallo no está en que no funcione la página, sino en que se muestren abiertamente los datos de conexión a la base de datos (excepto la contraseña). Uno de los errores más comunes (no es la primera vez que veo cagadas así) y más graves en seguridad es dar detalles sobre el funcionamiento del sistema, en este caso los de la BD. Está claro que el webmaster olvidó desactivar el reporte de errores... porque ¡en producción se debe desactivar el reporte de errores!

Es muy sencillo hacerlo: o bien editar el fichero de configuración de PHP, el famoso php.ini poniendo a 0 la directiva error_reporting o bien incluyendo la siguiente llamada en nuestros scripts:

error_reporting(0);

Esto no quita que haya que "perder de vista" los errores. PHP almacena todos los errores en el archivo de log especificado en la directiva de configuración error_log. Y te recuerdo que esa directiva, si no tienes acceso al php.ini de tu servidor, la puedes cambiar en directo a través de la función ini_set().

Más información sobre directivas de configuración en el manual oficial de PHP.


israelviana.es es propiedad de Israel Viana, escrito en Murcia (España). Puedes ponerte en contacto conmigo a través de la dirección de e-mail .com.
Información en RDF Metadatos Dublin Core Creative Commons License