Ofrecer sugerencias en tu WordPress usando opensearch

Creo que para ciertos blogs es muy útil ofrecer la opción de añadir OpenSearch para que la gente puede realizar búsquedas en nuestro blog. Algo también bastante útil es que nos ofrezca sugerencias de búsquedas (tal y como hace Google). Para ello tan sólo hay que añadir una opción al XML de OpenSearch y modificar nuestro functions.php.

En el fichero XML deberemos añadir la siguiente etiqueta:

Y añadir xmlns:suggestions="http://www.opensearch.org/specifications/opensearch/extensions/suggestions/1.1" a la etiqueta inicial OpenSearchDescription

Yo en este caso he preferido usar una URL amigable, sobre todo para practicar con el wp_rewrite, pero se podría hacer perfectamente con una variable con parámetros.

Una vez cambiado el opensearch.xml deberemos modificar el functions.php, para lo cual tendremos que coger el término que se quiere sugerir y buscar entre las categorías y tags y entre los títulos de los posts para así mostrar una unión de ambos. Las categorías se ordenarán por el número de veces que aparece y los posts por fecha descendente.

add_filter('init','suggestion_init');
add_filter('query_vars','suggestion_insertMyRewriteQueryVars');
add_filter('rewrite_rules_array','suggestion_rewrite');

// Acciones iniciales
function suggestion_init(){
  // Reescribe las reglas
  global $wp_rewrite;
  $wp_rewrite->rewrite_rules();

  // Obtiene el termino a buscar
  $req = explode('/', substr($_SERVER['REQUEST_URI'], 1), 2);
  if ($req[0] == 'suggestion') {
    $result = wp_cache_get( 'suggestion_'.$req[1] );
    if ( false == $result ) {
      global $wpdb;
      $list = array();
      // Recupera las 4 categorias/tags que coincidan
      $res = $wpdb->get_results("select name from $wpdb->terms t, $wpdb->term_taxonomy tx where name like '%".$req[1]."%' and t.term_id = tx.term_id group by name order by count desc limit 4");
      foreach ($res as $item) $list[] = $item->name; 
      // Recupera las 4 anotaciones que coincidan   
      $res = $wpdb->get_results("select post_title from $wpdb->posts where post_title like '%".$req[1]."%' order by ID desc limit 4");
      foreach ($res as $item) $list[] = $item->post_title;
      // Devuelve en formato JSON     
    	$result = json_encode(array($req[1], $list));
    	wp_cache_set( 'suggestion_'.$req[1], $result );
    } 

    echo $result;
    exit();
  }
}

// Permite que WP acepte un nueva parametro en la query de la URL
function suggestion_insertMyRewriteQueryVars($vars) {
    array_push($vars, 'suggestion');
    return $vars;
}

// Reescribe la regla
function suggestion_rewrite($rules)
{
	$newrules = array();
	$newrules['suggestion/([^/]+)/(.*)$'] = 'index.php?suggestion=$matches[1]';
	return $newrules + $rules;
}

En el código he añadido caché, porque es altamente recomendable, puediendo usar el plugin WP File Cache. También debo decir que me encontré con el problema del timeout que tiene Firefox para esperar la respuesta para la sugerencia, muy pequeño para mi servidor, por lo que si quieres modificarlo, busca el fichero nsSearchSuggestions.js y modifica el valor de _suggestionTimeout.

Peticiones en paralelo de CURL con PHP

Cuando realizamos una petición CURL normalmente usamos el método sencillo: hacer petición, esperar respuesta. ¿Pero que ocurre cuando queremos hacer varias peticiones de forma simultánea? por ejemplo para un crawler o una aplicación parecida. En estos casos tenemos que echar mano de curl_multi_*.

Si nos encontramos en esta situación podemos echar mano de la clase ParallelCurl, que de forma muy sencilla nos ayudará en nuestra tarea:

$pc = new ParallelCurl($max_requests, $curl_options);

// 3 peticiones en paralelo que empiezan simultaneamente
$pc->startRequest($url1, 'on_request_done');
$pc->startRequest($url2, 'on_request_done');
$pc->startRequest($url3, 'on_request_done');

$pc->finishAllRequests();

Parallel cURL execution in PHP

Vía / PHPDeveloper.org

Librería PHP para evitar SQL injection y XSS

Algo que debemos tener muy en cuenta cuando realizamos nuestra aplicación web es limpiar la entrada de datos de contenido malicioso, ya sea para SQL injection o cross-site scripting (XSS). Los frameworks suelen tener su propia librería que se encarga de ello, pero para aquellos que no usen framework o hagan una aplicación muy sencilla, esta librería les vendrá muy bien.

// Output an unsafe string, presumably user input
$xss = '';
echo 'If your entered your name as ' . $xss . ', we\'d be in trouble.
' . "\n"; // Sanitize that string, and output it safely $htmlContentContext = sgSanitizer::sanitizeForHTMLContent($xss); echo "But if we sanitize your name, " . $htmlContentContext . ", then all is well.
\n";

Genius Open Source Libraries

Vía / PHPDeveloper.org

Generar documentos MS Word con phpdocx

Me avisan los creadores de esta librería para la generación de ficheros docx de Microsoft, la cual dispone de una versión gratuita que ofrece bastantes características e incluso las más necesarias:

  • Formato de textos
  • Formato de párrafos
  • Enlaces externos
  • Cabeceras y pies de página
  • Imágenes
  • Listas
  • Tablas
  • Imágenes
require_once('../classes/cCreateDocx.inc');
$objDocx = new cCreateDocx();
$arrParamsText = array( 'b' => 'single', 'jc' => 'center’); //b for bold and jc for alignment
$objDocx->fAddText('Hello World.', $arrParamsText);
$objDocx->fCreateDocx('hello_world.docx');

PHPDOCX

HTML5 websockets con PHP

Ya hace tiempo hable lo de los websockets con HTML, los cuales solo se pueden usar en Safari, Chrome y la beta de Firefox 4, pero además el servidor necesita que los soporte. Para poder usarlos en nuestras aplicaciones PHP se puede hacer un apaño con la siguiente librería:

log("Handshaking...");
list($resource,$host,$origin) = getheaders($buffer);
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
           "Upgrade: WebSocket\r\n" .
           "Connection: Upgrade\r\n" .
           "WebSocket-Origin: " . $origin . "\r\n" .
           "WebSocket-Location: ws://" . $host . $resource . "\r\n" .
           "\r\n";
$handshake = true;
socket_write($socket,$upgrade.chr(0),strlen($upgrade.chr(0)));

phpwebsockets

Vía / bo! hu? co.

Filtra y recupera de forma segura $_GET y $_POST en PHP

Hace tiempo comentaba cómo filtrar variables, ahora, usando los mismos filtros, podremos filtrar y realizar sanitize de $_GET y $_POST y asegurarnos de que no nos metan valores peligrosos en las entradas de nuestras aplicaciones.

Para ello haremos uso de filter_input y filter_input_array, pudiendo usar una lista de filtros bastante amplia.

$var = filter_input(INPUT_GET, 'parametro_entrada', FILTER_SANITIZE_STRING);
$var = filter_input(FILTER_GET,'paramentro_sin_filtrar', FILTER_UNSAFE_RAW);
$args = array(
  'product_id'   => FILTER_SANITIZE_ENCODED,
  'component'    => array('filter'    => FILTER_VALIDATE_INT,
    'flags'     => FILTER_REQUIRE_ARRAY, 
    'options'   => array('min_range' => 1, 'max_range' => 10)
    ),
  'versions'     => FILTER_SANITIZE_ENCODED,
  'doesnotexist' => FILTER_VALIDATE_INT,
  'testscalar'   => array(
    'filter' => FILTER_VALIDATE_INT,
    'flags'  => FILTER_REQUIRE_SCALAR,
    ),
  'testarray'    => array(
  'filter' => FILTER_VALIDATE_INT,
  'flags'  => FILTER_REQUIRE_ARRAY,
  )
);
$myinputs = filter_input_array(INPUT_POST, $args);

Never Use $_GET Again

Vía / PHPDeveloper.org

Mostrar la media semanal de lectores de Feedburner con PHP

Parece que Feedburner está dando problemas, y ahora parece que te muestra los usuarios que leyeron tu blog el día anterior, y no el numero de suscritos como se mostraba anteriormente, por lo que a los que no escribimos posts a diarios, el número que muestra Feedburner puede variar muchísimo.

Por ello, puede ser interesante mostrar la media semanal o mensual, como queramos, de los usuarios que nos leen:

function get_average_readers($feed_id,$interval = 7){
	$today = date('Y-m-d', strtotime("now"));
	$ago = date('Y-m-d', strtotime("-".$interval." days"));
	$feed_url="https://feedburner.google.com/api/awareness/1.0/GetFeedData?uri=".$feed_id."&dates=".$ago.",".$today;
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_URL, $feed_url);
	$data = curl_exec($ch);
	curl_close($ch);
	$xml = new SimpleXMLElement($data);
	$fb = $xml->feed->entry['circulation'];

	$nb = 0;
	foreach($xml->feed->children() as $circ){
		$nb += $circ['circulation'];
	}

	return round($nb/$interval);
}
$nb = get_average_readers('miusuario');
echo "tengo ".$nb." suscriptores RSS";
// o
$nb = get_average_readers('miusuario', 30);
echo "tengo ".$nb." suscriptores RSS";

How to get a more relevant Feedburner count

Vía / DZone

Conocer a quien le gusta tu post en Google Reader con PHP y CURL

No es difícil ver en blogs los retweets que hace la gente de tus posts y que estos se muestren en los comentarios. ¿Por qué no hacer lo mismo con Google Reader?. En Google Reader, es posible ver a quienes les gustan tus posts (si tienes tu propio blog en el reader), lo cual te puede ayudar a saber si tus posts interesan o no, o encontrar usuarios con gustos parecidos y así seguir sus recomendaciones y poder leer posts interesantes.

Google suele dar complicaciones a la hora de hacer uso de sus webs, pero si usamos su API de ClientLogin lo podemos hacer de forma sencilla.

Primero tendremos que loguearnos en Google, para lo cual tendremos que indicar nuestro usuario y contraseña y los datos posts que tendremos que pasarle:

$usuario = 'micorreo@gmail.com';
$password = 'mipassword';
$postdata = 'service=reader&Email='.urlencode($usuario).'&Passwd='.urlencode($password).'&continue=http://www.google.com/reader/&service=reader';

Luego nos logueamos y obtenemos el valor de Auth para usarlo en el resto de peticiones usandolo en el header de la petición HTTP:

$ch = crl_init($url);
crl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)');
crl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
crl_setopt ($ch, CURLOPT_COOKIEJAR, './cookie');
crl_setopt ($ch, CURLOPT_COOKIEFILE, './cookie');
crl_setopt ($ch, CURLOPT_POSTFIELDS, $postdata);
crl_setopt ($ch, CURLOPT_POST, 1);
crl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false );
crl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false );

$url = 'https://www.google.com/accounts/ClientLogin';
crl_setopt($ch, CURLOPT_URL, $url);
crl_setopt($ch, CURLOPT_FOLLOWLOCATION, 4);
$res = crl_exec($ch);
preg_match('/Auth=(.*)/', $res, $m);
$auth = $m[1];

Después realizo la búsqueda, para lo cual busco el título del post y le añado el nombre del blog (chapu a la espera de encontrar algo mejor), por ejemplo “Stanford%20Javascript%20Crypto%20Library%20sentidoweb

$url = 'https://www.google.com/reader/api/0/search/items/ids?q=Stanford%20Javascript%20Crypto%20Library%20sentidoweb&output=json';
crl_setopt($ch,CURLOPT_HTTPHEADER,array('Authorization: GoogleLogin auth='.$auth)); 
crl_setopt($ch, CURLOPT_POST, 0);
crl_setopt($ch, CURLOPT_URL, $url);
$res = crl_exec($ch);
$datos = json_decode($res);

De aquí obtengo el ID del post dentro de Google Reader, y con ese ID obtengo los likers y muestro sus fotos:

if (isset($datos->results[0]->id)) {
  $url = 'https://www.google.com/reader/api/0/stream/items/contents?client=scroll';
  crl_setopt($ch, CURLOPT_POST, 1);
  crl_setopt ($ch, CURLOPT_POSTFIELDS, 'i='.$datos->results[0]->id);
  crl_setopt($ch, CURLOPT_URL, $url);
  $res = crl_exec($ch);
  
  $datos = json_decode($res);
  if (isset($datos->items[0]->id)) {
    $id = $datos->items[0]->id;
    $id = end(explode('/', $id));
    $url = 'https://www.google.com/reader/api/0/item/likers?i=tag%3Agoogle.com%2C2005%3Areader%2Fitem%2F'.$id.'&output=json&client=scroll';
    crl_setopt($ch, CURLOPT_POST, 0);
    crl_setopt($ch, CURLOPT_URL, $url);
    $res = crl_exec($ch);
    $data = json_decode($res);
    if (isset($data->friends)) {
      foreach($data->friends as $friend) {
        if (isset($friend->photoUrl)) {
          echo '';
        }
      }
    }
  }
}

De los likers se pueden obtener más datos, y si se hace un var_dump($data->friends) se podrá ver cómo acceder a ellos.

Está claro que un plugin para WordPress estaría bien, pero es algo más complejo si se quiere hacer bien, porque se debería obtener los ids segun URLs, entre otras cosas.

Clase para usar Google Translate desde PHP

La verdad es que tiro de vez en cuando Google Translate, pero sobre todo para temas personales, para alguna aplicación web no creo que sea muy útil, entre otras cosas porque a veces las traducciones en algunos casos son un poco raras, pero nunca vienen mal tener esta librería para realizar traducciones.

El uso es muy sencillo:

require_once('googleTranslate.class.php');
$gt = new GoogleTranslateWrapper();
/* Traduce una pagina web */
$page_contents = file_get_contents('http://some-web-page/');
/* Traduce al frances */
echo $gt->translate(page_contents , "fr", "en");
/* Si hay error lo muestra */
if(!$gt->isSuccess()) {
    echo $gt->getLastError();
}

Google Translation PHP wrapper

Vía / PHPDeveloper.org

Actualización: me avisa Javier Martín que él ha implementado una librería parecida que añade algunas características interesantes como independencia de CURL.

Crear un sistema de notificación web con XMPP y PHP

Completísimo tutorial que nos explica como realizar un sistema de notificación web en tiempo real mediante XMPP y PHP. El tutorial nos explica cómo funciona el XMPP, cómo instalar Openfire (yo he usado ejabberd y este no le conozco, si alguien lo conoce que me comente que tal).

En el ejemplo hará uso de jQuery y Strophe para acceder al servidor XMPP.

Build a web-based notification tool with XMPP

Gracias Gerardo por el aviso