Tutorial sobre Facebook Connect

Facebook Connect es una API de FB que nos permite usar nuestra cuenta de FB en otras webs. A mi personalmente, la ayuda que ofrece FB no me apasiona demasiado, y cuando quieres realizar algo con Facebook Connect te tienes que pelear bastante con cosas que haces mal o que te faltan por hacer. Por ello, creo que este tutorial es bastante interesante para aquellos que quieren desarrollar algo con FB Connect y no saben por dónde empezar.

El tutorial nos mostrará los siguientes puntos:

  1. Crear una aplicación en Facebook
  2. Copiar en tu sitio el fichero xd_receiver.htm
  3. Descargar la API PHP de FB en tu web
  4. Modificar tu tabla de usuarios
  5. Crear una página para loguearse con FB Connect
  6. Enlazar a fbclogin.php desde tu login
  7. Otros puntos a seguir

Facebook Connect Tutorial

Vía / PHPDeveloper.org

|

Laboratorio: leer cualquier tipo de imágen en PHP de forma sencilla

Uno de los problemas con los que nos solemos encontrar es leer imágenes en PHP mediante la librería GD sin estar seguros de que tipo de fichero es: JPEG, GIF o PNG. Normalmente solemos mirar la extensión y según sea, abrir el fichero con alguno de los métodos que ofrece GD. Pero hay una solución más sencilla: leer el fichero en un string y crear la imagen usando ese script:

$img_content = file_get_contents($image);
$im = imagecreatefromstring($img_content);
// Para saber el tipo de imagen por si lo necesitamos
$imgtype = exif_imagetype($image);

Laboratorio: postear en WordPress mediante GTalk o Jabber (mejorado)

Hace tiempo hice un script para PHP que permitía postear en WordPress usando Jabber (por ejemplo GTalk). Ahora he mejorado un poco el script para que admita poner categorías (si no las hay, las crea) y subir imágenes mediante una URL (ajustándolo a un ancho máximo).

Existen distintas palabras claves para separar los distintos elementos del post dentro del texto que se envía: titulo, contenido, tags, categorias e image. Las distintas secciones se separan con ‘##’ para diferenciarlas unas de otras y se separarán por dos puntos ‘:’ formando pares clave:valor. Un ejemplo para escribir en el IM sería el siguiente:

titulo:Titulo del post##contenido:Lorem Ipsum... con todo el HTML que querramos##tags:etiqueta1, etiqueta2, etiqueta3##categorías:Categoria1,Categoria2#image:http://servidor.com/ruta/imagen.png

El código principal es el siguiente:

$e

";'), explode("\n", $contenido))); } if (isset($image)) { $img_content = file_get_contents($image); $im = imagecreatefromstring($img_content); $w = imagesx($im); $h = imagesy($im); $imgtype = exif_imagetype($image); if ($w > $max_width) { $image_p = imagecreatetruecolor($max_width, intval($h*$max_width/$w)); imagecopyresampled($image_p, $im, 0, 0, 0, 0, $max_width, intval($h*$max_width/$w), $w, $h); ob_start(); imagepng($image_p); $img_content = ob_get_contents(); ob_end_clean(); $imgtype = IMAGETYPE_PNG; $w = $max_width; $h = intval($h*$max_width/$w); } $imgname = "img".time().image_type_to_extension($imgtype); $f = new xmlrpcmsg('metaWeblog.newMediaObject', array(php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña), php_xmlrpc_encode(array('name'=>$imgname, 'bits'=>new xmlrpcval($img_content, 'base64')))) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if(!$r->faultCode()) { $v=$r->value(); $datos = simplexml_load_string($v->serialize()); $url = $datos->xpath('//member/name[. ="url"]/following-sibling::*/string'); $contenido = ''.$contenido; } else { return "Error a la hora de subir la imagen: $image [".$r->faultString()."]"; } } //$contenido .= mb_convert_encoding($contenido, 'UTF-8'); $f = new xmlrpcmsg('wp.getUsersBlogs', array(php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña)) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if(!$r->faultCode()) { $v=$r->value(); $datos = simplexml_load_string($v->serialize()); $blogid = $datos->xpath('//member/name[. ="blogid"]/following-sibling::*/string'); $blogid = is_array($blogid) ? (string) $blogid[0] : (string) $blogid; $f = new xmlrpcmsg('metaWeblog.newPost', array(php_xmlrpc_encode($blogid), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña), php_xmlrpc_encode(array('title'=>$titulo, 'description'=>$contenido, 'mt_keywords'=>$tags)), php_xmlrpc_encode(1)) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->request_charset_encoding = 'UTF-8'; $c->setDebug(0); $r=&$c->send($f); if($r->faultCode()) { return "Ha habido un error al intentar crear un nuevo post [".$r->faultString()."]"; } else { $datos = simplexml_load_string($r->serialize()); $postid = $datos->xpath('//value/string'); $postid = is_array($postid) ? (string) $postid[0] : (string) $postid; $f = new xmlrpcmsg('metaWeblog.getPost', array(php_xmlrpc_encode($postid), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña)) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if($r->faultCode()) { return "Ha habido un error al intentar recuperar información sobre el post insertado [".$r->faultString()."]"; } else { $datos = simplexml_load_string($r->serialize()); $permalink = $datos->xpath('//member/name[. ="permaLink"]/following-sibling::*/string'); $permalink = is_array($permalink) ? (string) $permalink[0] : (string) $permalink; $title = $datos->xpath('//member/name[. ="title"]/following-sibling::*/string'); $title = is_array($title) ? (string) $title[0] : (string) $title; if (isset($categorias)) { $f = new xmlrpcmsg('mt.getCategoryList', array(php_xmlrpc_encode($postid), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña)) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if(!$r->faultCode()) { $datos = simplexml_load_string($r->serialize()); $catsblog = $datos->xpath('//struct'); foreach(explode(',', $categorias) as $cat) {$cats[$cat] = null;} foreach($catsblog as $cat) { foreach($cats as $_cat=>$val) { if (strtolower((string) $cat->member[1]->value->string) == strtolower($_cat)) { $cats[$_cat] = (string) $cat->member[0]->value->string; } } } foreach($cats as $cat=>$val) { if (!$val) { $f = new xmlrpcmsg('wp.newCategory', array(php_xmlrpc_encode($postid), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña), php_xmlrpc_encode(array('name'=>$cat, 'slug'=>str_replace(' ', '_', strtolower($cat)), 'parent_id'=>1, 'description'=>''))) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if(!$r->faultCode()) { $datos = simplexml_load_string($r->serialize()); $catid = (string) $datos->xpath('//int'); $cats[$cat] = $catid; } else { return "Ha habido un error a la hora de crear la categoría $cat"; } } } foreach($cats as $val) { $catlist[] = array('categoryId'=>$val); } $f = new xmlrpcmsg('mt.setPostCategories', array(php_xmlrpc_encode($postid), php_xmlrpc_encode($blog_usuario), php_xmlrpc_encode($blog_contraseña), php_xmlrpc_encode($catlist)) ); $c=new xmlrpc_client($blog_xmlrpc_path, $blog_url, $blog_puerto); $c->setDebug(0); $r=&$c->send($f); if($r->faultCode()) { return "Hubo un error a la hora de modificar las categorías del post"; } } else { return "Ha habido un error al intentar recuperar las categorias del blog"; } } return "Se ha publicado '$title': $permalink"; } } } else { return "Ha habido un error al intentar recuperar información sobre el blog [".$r->faultString()."]"; } } //Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports //If this doesn't work, are you running 64-bit PHP with < 5.2.6? $conn = new XMPPHP_XMPPOld($jabber_host, $jabber_puerto, $jabber_usuario, $jabber_contraseña, 'xmpphp', $jabber_host, false, XMPPHP_Log::LEVEL_ERROR); $conn->autoSubscribe(); $conn->useEncryption(false); try { $conn->connect(); while(!$conn->isDisconnected()) { $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start')); foreach($payloads as $event) { $pl = $event[1]; switch($event[0]) { case 'message': if (trim($pl['body']) == '') break; $conn->message($pl['from'], $body=post($pl['body']), $type=$pl['type']); if($pl['body'] == 'quit') $conn->disconnect(); if($pl['body'] == 'break') $conn->send(""); break; case 'presence': // print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n"; break; case 'session_start': //print "Empezamos\n"; $conn->getRoster(); $conn->presence($status="Asonesss jefeeeeeee!"); break; } } } } catch(XMPPHP_Exception $e) { echo 'ERROR:'; die($e->getMessage()); }

Podéis bajaros el código aquí.

Librería SSH para PHP

Para aquellos que quieran desarrollar una aplicación que tenga acceso mediante SSH, le vendrá bien esta librería, aunque con la limitación de que sólo admite SSH1. Esto puede ser un impedimento, pero la librería que hay para SSH2 es PECL, lo cual también puede ser un impedimento a la hora de instalar.

require_once ('ssh_in_php.php');

$host = "127.0.0.1";
$port = 22;
$user = "user";
$password = "pass";

try {
    $ssh = new SSH_in_PHP($host,$port);
    $ssh->connect($user,$password);

    $cycle = true;
    while ($cycle) {
        $data = $ssh->read();
        echo $data;
        if (ereg('\$',$data)) {
            $cycle = false;
        }
    }
    $ssh->write("ls -al\n");
    $cycle = true;
    while ($cycle) {
        $data = $ssh->read();
        echo $data;
        if (ereg('\$',$data)) {
            $cycle = false;
        }
    }

    $ssh->disconnect();

} catch (SSHException $e) {
    echo "An Exception Occured: {$e->getMessage()} ({$e->getCode()})\n";
    echo "Trace: \n";
    echo print_r($e->getTrace());
    echo "\n";
} 

SSH in PHP

Consejos para desarrollar en la nube

Aunque no soy muy fan de la moda del cloud computing, quizás porque se habla de ello muchas veces un tanto a la ligera, si que creo que desarrollar aplicaciones en arquitecturas basadas en la nube es algo que en el futuro tendremos que realizar (más basado en hosting que en servicios). Por ello estos consejos pueden ser interesantes:

  • Las máquinas son igualmente inseguros: es por ello que tampoco merece la pena gastar más medios en máquinas más críticas como la BD. Toda máquina falla, por lo que es mejor diseñar la arquitectura teniendo esto en mente para poder recuperarnos de una caída más facilmente.
  • Una aplicación en la nube depende absolutamente de la red y el IO, por lo que no es buena idea tenerlos “físicamente” muy lejos. Esto es uno de los motivos por los que yo, personalmente, no creo que tenga mucho éxito el concepto nube pensado como aplicación en Google y BD en Amazon (o parecido), algo que muchos se dedican a “vendernos”.
  • La red no es fiable, por lo que es conveniente tener sistemas de monitorización para saber qué ocurre en las máquinas y si tienen accesos unas a otras

El post original explica mejor cada punto y ofrece otros consejos más, que recomiendo leer.

Designing applications for cloud deployment

HyperGraphDB: otra base de datos de grafos

Si hace tiempo hablé de OQGraph, un plugin para MySQL para almacenar grafos, en este caso se trata de una BD diseñada específicamente para ello. HyperGraphDB es una base de datos orientada a inteligencia artificial y redes sociales que mediante el almacenamiento de grafos facilita aplicaciones de este estilo.

Se trata de una BDopen source realizada en Java que es extensible, portable, distribuida y incrustable. Y cuyas características principales son:

  • Un segmento puede apuntar a más de un nodo.
  • La unidad básica de almacenamiento se llama átomo y cada átomo tiene su tipo y puede apuntar a ninguno o más átomos.
  • Los tipos de datos se manejan mediante un sistema almacenado en una estructura hipergrafo. Los tipos son en sí átomos pero con un rol particular.
  • Es accesible por cualquier lenguaje de programación y el sistema de almacenamiento usado a bajo nivel se basa en BerkeleyDB.
  • Procesos distribuidos basados en P2P para replicación y particionamiento de datos.

Vamos, una joyita para implementar una red social, aunque estaría bien conocer pruebas de rendimiento.

HyperGraphDB

Vía / High Scalability

oEmbed: formato para URLs de contenido EMBED

Bueno, el título es algo difícil de entender, pero el concepto es fácil, oEmbed es un formato para devolver información sobre URLs que muestran contenido incrustable: imágenes, vídeos, …

Por ejemplo para Flickr sería:

http://www.flickr.com/services/oembed/?url=http%3A//www.flickr.com/photos/bees/2341623661/

Y el resultado en formato JSON sería:

{
  "version": "1.0",
  "type": "photo",
  "width": 240,
  "height": 160,
  "title": "ZB8T0193",
  "url": "http://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg",
  "author_name": "Bees",
  "author_url": "http://www.flickr.com/photos/bees/",
  "provider_name": "Flickr",
  "provider_url": "http://www.flickr.com/"
}

Existen librerías PHP y jQuery que funcionan con proxy para obtener la información.

Learning oEmbed: Convert Links Into Embedded Content