Eventos en MySQL (planificar tareas por fecha y hora)

Seas un dba o seas un programador (y yo en mi caso soy ambas cosas) la automatización de tareas es algo no sólo útil, sino a veces indispensable. Al estilo de los cron jobs de Linux MySQL nos ofrece la posibilidad de definir una acción o serie de acciones que se repita periódicamente o se ejecuten en un momento concreto.

Lo primero es habilitar a nuestro servidor MySQL para que pueda hacerlo, con la siguiente línea:

SET GLOBAL event_scheduler = ON;

Si usas PHPMyAdmin tienes por ahí un botón para activarlo, no necesitas comandos.

Bueno, la sintaxis básica de la creación de eventos, y a la guía de usuario de MySQL me remito, es la siguiente:


CREATE
[DEFINER = { user | CURRENT_USER }]
EVENT
[IF NOT EXISTS]
event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO sql_statement;

schedule:
AT timestamp [+ INTERVAL interval] ...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp [+ INTERVAL interval] ...]

interval:
quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}

Ok, visto así, acojona. Pero no todos los parámetros son obligatorios, y explicado es fácil. Reamente necesitas sólo un nombre para el event_name, una condición para que se cumpla el ON SCHEDULE y un código sql que ejecutar después del DO. Te meto un ejemplillo sacado del curro, por ejemplo:

CREATE EVENT `close_expired_campaigns` 
ON SCHEDULE EVERY 1 DAY STARTS '2013-01-21 00:00:01' 
ON COMPLETION NOT PRESERVE ENABLE 
DO update tabla
set valido = 0
where
DATE_ADD(fechaCreacion, INTERVAL 45 DAY)<NOW() and valido=1;

Tiene algo más de código porque lo genera automáticamente PHPMyAdmin pero la idea es que crea un evento con ese nombre que, cada día un segundo después de medianoche actualice la tabla seleccionada poniendo a cero la columna indicada cuando se cumpla una condición (en este caso, que hayan pasado 45 días desde la creación). Bueno, como el código es tocho, os pongo debajo un ejemplo con la sintaxis más básica posible. Imaginemos una funcion que actualiza el saldo de una cuenta diaria de gastos a 1000 euros:

CREATE EVENT e_ActualizaSaldoDiario
ON SCHEDULE EVERY 1 DAY STARTS ‘2013-01-01 00:00:00’
DO UPDATE gastos SET saldo = 1000

Los dos ejemplos son recurrentes, se ejecutan todos los días a una hora (el parámetro Starts no permite definir el momento en que comenzará a ejecutarse, acuérdate de ponerlo porque si pones solo EVERY 1 DAY debería fallar, y no tiene por qué ser cada día, puedes poner un periodo concreto de horas, minutos, días, semanas…). Existe la posibilidad de que un evento se ejecute sólo una vez, en el momento que le indiques y luego se borre. Por ejemplo imaginemos que quieres que dentro de 6 horas se borren todos los registros del año 2012:

CREATE EVENT e_Borra2012
ON SCHEDULE AT now() + INTERVAL 6 HOUR
DO DELETE cuentas WHERE year = 2012

Y en estos tres ejemplos sólo hemos utilizado sentencias de SQL simples, pero imagina la potencia (y los riesgos de joderlo todo si la cagas, claro está) si los combinamos con procedimientos almacenados y con disparadores… Me imagino una bombillita alumbrando tu cabeza, y todas las cuestiones de mantenimiento y actualización que se te están ocurriendo.

Seleccionar filas aleatoriamente en MySQL

Existen varias opciones a la hora de seleccionar filas de forma aleatoria en MySQL, haciendo uso de la función RAND(). Vamos a imaginar que quieres obtener 3 filas aleatorias de una base de datos. Lo más sencillo es usar la siguiente sintaxis:

SELECT * FROM tabla ORDER BY RAND() LIMIT 3;

El problema de este método es que tiene que generar una tabla temporal completa con todos los datos de la tabla original reordenados aleatoriamente… en fin, que es una sangría de recursos. Existe una opción, que es generar sólo una tabla con los registros que queramos, mediante una subconsulta. Tal que así:

SELECT * FROM tabla WHERE RAND()<(SELECT ((3/COUNT(*))*10) FROM tabla) ORDER BY RAND() LIMIT 3;

Un método que sigue gastando muchos recursos, pero menos al crear sólo una tabla temporal con la cantidad de filas que necesitamos (cantidad que necesitamos / total * 10).

Pero si tenemos un índice autonúmerico en nuestra tabla podemos ahorrar más recursos:

SELECT t.* FROM tabla AS t JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM tabla)) AS id) AS x WHERE t.id >= x.id LIMIT 3;

Este método no genera un valor aleatorio para cada fila consultada, por lo que ahorra muchos recursos en el servidor. Eso sí, no te dará los registros ordenados aleatoriamente, sino que será registros secuenciales, pero comenzando en un valor aleatorio. Es decir, los dos primeros podrían devolverte las filas 1, 21 y 42, este en cambio te devolvería 1,2,3 – 41,42,43… si quieres que sea un resultado aleatorio de verdad deberías realizar la consulta varias veces con limit 1… con lo que el ahorro de recursos se va al carajo. Además, puede fallar si hay algún hueco en la secuencia del campo autonumérico (por ejemplo, si has borrado un valor).

Una última opción, en lugar de generar el valor aleatorio a nivel de base de datos generarlo antes, en el código del servidor. Un ejemplo en PHP (que también tiraría de id autonumérica y que también daría problemas si tuviera huecos, ojo):

$result = $mysqli->query("SELECT * FROM tabla WHERE id in ROUND(".lcg_value()."*(SELECT COUNT(*) FROM tabla)) LIMIT 1")

El ejemplo de arriba nos devolvería una fila aleatoriamente, por lo que bastaría con repetirla en un bucle tantas veces como necesitemos (en este caso el problema vendría de la cantidad de conexiones que hay que abrir a la base de datos). Puedes intentar implementar lcg_value() en los otros métodos citados, y comprobar si el rendimiento mejora.

Consumir JSON en Android

Si el otro día hablaba de crear y consumir JSON en PHP, ahora toca su turno a Android.

¿Por qué usar JSON? Bueno, es un formato de intercambio de datos independiente de cualquier lenguaje y autodefinido. Ok, XML también, pero JSON es más ligero y fácil de parsear.

Existen varias librerías para JSON en Android, pero en el ejemplo usaremos los métodos nativos del SDK: las clases JSONArray y JSONObject.

JSONArray necesita recibir una cadena JSON para devolvernos un array de JSONObject. Supongamos que hemos recibido esos datos (sea de un web service o de un archivo) y los tenemos en una variable llamada data:

JSONArray jsArray = new JSONArray(data);

Ahora ya tenemos los datos dentro de un JSONArray. ¿Cómo lo recorremos? Pues con un for:

for (int i = 0; i < jsArray.length(); i++) {
    JSONObject jsObject = jsArray.getJSONObject(i);
    
    int intejemplo = jsObject.getInt(atributo1);
    String stringejemplo = jsObject.getString(atributo2);
}

El problema es que trabajar así puede ser muy plomífero, tratando los datos a mano, uno a uno. Existen librerías que te pueden echar un cable en esto, dándote mayores funcionalidades. En este caso las dos más populares son GSon y Jackson. En los enlaces podrás ver documentación sobre su uso.

El saber manipular JSON se torna capital cuando quieres trabajar con servicios web, o para comunicar apps con widgets.

El peor quinteto titular NBA de la última década (2002-2012)

En mi extraña afición por seguir a jugadores mediocres se me ha ocurrido  la idea de formar… el peor equipo NBA de la última década. Pero tiene una condición este quinteto de la infamia, y es que sería muy fácil coger a cinco tíos que no valen pa’ ver que tuvieron contrato de 10 días para cubrir alguna lesión… nooooooooo!! El equipo estará formado por jugadores que fueron starters alguna temporada. Eso hace más sangrante la selección.

  • Smush Parker (base): «No debería haber jugado en la NBA, pero no teníamos dinero para pagar a un base» con estas palabras Kobe Bryant describió la estancia de Smush Parker en los Lakers. Y es que durante dos temporadas este jugador de 1.92 ocupó el puesto de base titular en los Lakers, con promedios de alrededor de 11 puntos, en su primera temporada aderezados con 3.7 asistencias y en la segunda con 2.8, mostrando un pequeño bajón en su rendimiento. El joven Parker no fue drafteado y vivió de contratos de 10 días hasta que recaló en la liga griega, donde hizo un papel decente en el Aris de Salónica. Su rendimiento en Europa hizo que los Pistons se intresaran por él, si bien le cortarían mediada la campaña y acabaría en Phoenix. Al año siguiente recalaría en unos desestructurados Lakers donde ser haría con el puesto de titular durante su primera temporada y parte de la segunda, hasta que Jordan Farmar (que tampoco era ninguna maravilla) le desplazó de la rotación. Tras eso recalaría en Miami con unos decepcionantes 4.7 puntos y 1.7 asistencias hasta ser cortado por incidentes varios. Acabaría la temporada en los Clippers, donde optarían por no renovarle y firmaría por Denver, donde ya en pretemporada le dieron la patada. Tas eso pasó por la D-League, Irán, Venezuela, Rusia, Grecia (sin lograr acercarse a los registros de su primera etapa)… hasta asentarse al fin en la liga China.
    Smush Parker
    Smush y Kobe, si hasta parecía que se llevaban bien

     

  • Marco Belinelli (escolta): No pongo en duda que Belinelli pueda dar un cierto rendimiento como especialista tirador de triples saltando del banquillo. Debutó siendo un adolescente en la liga italiana, en el Virtus Bolonia, y poco después firmaría por el eterno rival: el Fortitudo Bolonia. No haría malos partidos con la selección y en la Euroliga y decidió dar el salto a la NBA… craso error. Con un físico poco poderoso y la misma agresividad e intesidad defensiva que su compatriota Bargnani (o sea, nula) la muñeca de Marco no es argumento para darle una sólida carrera NBA. Pasó por Golden State y Toronto sin pena ni gloria, pero al recalar en New Orleans, por algún extraño motivo (lesiones, traspasos), acabó haciéndose un hueco en el equipo titular. En su primera temporada promedió 10.4 puntos y en la siguiente 11.8, pero su indolencia en labores defensivas llevaron a que no se le renovara. Finalmente el chaval con cara de extra de peli de mafiosos ha recalado en los Bulls como sustituto de Kyle Korver, es decir, experto triplista desde el banquillo, un rol que le pega mucho más que el de jugador titular.
    Marco Belinelli
    Hay quien le ve parecido conn Stallone

     

  • Luke Walton (alero): Ser hijo de uno de los 50 mejores jugadores de la historia debe ser duro cuando te dedicas al baloncesto. Toda la presión de ser comparado constantemente con tu padre…  y si eres Luke Walton, más. Porque son muchos los que le han designado como «el peor jugador de la historia de la NBA«. Si algo sorprende de este chaval es que no tenía ninguna virtud específica: no era un buen tirador, no defendía especialmente bien, no reboteaba, no asistía… ni siquiera era uno de esos jugadores que hace un poco de todo, porque este lo hacía, pero de forma mediocre. Pero, durante una temporada, Luke fue el alero titular de los Lakers. Promedió 11 puntos, 5 rebotes y 4 asistencias en 33 minutos, y al año siguiente volvió al banquillo, que es donde hacía menos daño. Lo verdaderamente sorprendente es que a pesar de su mediocridad haya ganado dos anillos (2009 y 2010 con Lakers) y haya logrado firmar buenas renovaciones (actualmente tiene un contrato de 6 millones de dólares) y es que tras su año titular renovó por 30.000.000 más incentivos para 6 temporadas.
    Luke Walton
    Luke «dos anillos» Walton

     

  • Darko Milicic (ala-pivot): Lo curioso de Milicic es que ni siquiera había destacado en las ligas europeas cuando, con 18 años, se presentó al draft. Era una promesa, alto y bastante ágil, por lo que algún iluminado en Detroit decidió escogerle con el número 2 del draft de 2003 (por encima de Chris Bosh, Dwayne Wade, Carmelo Anthony…). Y los inicios no fueron malos para Darko. Ok, no pintaba una mierda en el equipo y promediaba 5 minutos por partido, pero en su primer año serían campeones de la NBA y el siguiente finalistas. Tras su tercer año le mandaron a Orlando, donde siguió en el banquillo, aunque participando ya en la rotación habitual del equipo, a veces como pivot a veces como cuatro. Y de repente, algún otro iluminado le ficha para ser la pareja interior de Pau Gasol en los Grizzlies, si bien acabaría jugando de cuatro cuando Pau fue traspasado a Lakers mediada la campaña. Darko lo había logrado, el número 2 del draft al fin era titular: 7 puntos, 6 rebotes y 1.5 tapones… bueno, hacía peores números que cuando era suplente en Orlando, aunque es cierto que tampoco jugaba muchos más minutos. Al final en Memphis le mandaron al banquillo en su segundo año. De ahí pasó a los Knicks que a media campaña le mandarían a los T-Wolves y… sorpresivamente otra vez volvió a ser titular, esta vez al lado de Kevin Love: 9 puntos, 5 rebotes y 2 tapones… bueno, al menos ponía tapones, ya es una mejora respectoa a su pasado. Al año siguiente comenzaría como titular hasta que una lesión le apartó del equipo, dándole la oportunidad a Pekovic… que se acabó quedando con el puesto porque es bastante mejor jugador. Este año recaló en los Celtics donde, tras sólo disputar un partido, pidió que se rescindiera su contrato para volver a Europa. Actualmente se encuentra sin equipo. Hay que destacar que con la selección Serbia sí hizo un gran papel en el mundial de 2006, y tuvo algunas actuaciones destacadas con su escuadra nacional, al menos hasta que se le fue la pinza, amenazó de muerte a un árbitro, le metieron un tremendo paquete y acabó renunciando a su selección.
    Darko Milicic
    Darko en labores defensivas

     

  • Kwame Brown (pivot): Kwame me plantea siempre tres dudas. La primera ¿cómo logró ser número uno del draft por encima de, por ejemplo, Pau Gasol?. La segunda ¿cómo ha logrado mantenerse en la NBA y firmando contratos de bastante monto?. Y la tercera ¿el ojeador que le eligió y los directivos que le renovaron estarán dirigiendo el tráfico de las alcantarillas siberianas? Y es que Kwame es probablemente el peor número uno de la historia del draft. Es un jugador versátil, ya que al igual que el antes citado Milicic puede jugar de 4 o de 5, y lo hace igual de mal en ambos puestos. A pesar de eso ha logrado ser titular en Washington Wizards, Lakers y Bobcats (lo que me hace pensar si será familiar de Michael Jordan). Hay que resaltar que cuando promedió 11 puntos y 7 rebotes en su tercera temporada algunos pensamos que igual al final podía llegar a ser un jugador solvente, pero ya se encargó él de aclararnos al año siguiente que no, que sólo era un espejismo de mejora. En su primer año en los Lakers hizo unos play-off decentes, y de nuevo volvió a parecer que igual había esperanza… pero no, otro espejismo. Salió de Los Ángeles en la operación del traspaso de Gasol, donde le cortaron y, finalmente, fue dando bandazos por Detroit, Charlotte (donde recuperó otra vez el status de titular… con los bajos resultados de siempre), Golden State, Milwaukee y, este año, los Sixers, donde al fin han encontrado su puesto ideal: el fondo del banquillo.
    Kwame Brown
    Y aquí Kobe pensaba ¿por qué me tuve que enfadar con Shaq?

     

  • Michael Olowokandi (el sexto hombre): Finalmente, un refuerzo de banquillo para este equipo. Y es que Michael Olowokandi es uno de los pocos que le puede disputar a Kwame Brown el título de peor número uno de la historia. Hay que reconocer que en la temporada 2002-2003 estaba jugando más o menos bien (no para justificar un número uno), pero se lesionó… y jamás volvió a dar muestras de poder volver a rendir. Ojo, no quiero decir con esto que fuera una gran progresión cortada por las lesiones (como ha habido muchos casos), más bien que el tío era vago. Tras agotar la paciencia de los Clippers fue traspasado a los T-Wolves y luego a Boston, donde se retiraría a los 31 años rodeado de problemas personales, escribiendo su nombre en la historia de la NBA (como una de las mayores cagadas del draft, pero lo escribió).
    Michael Olowokandi
    Michael Olowokandi, elegido por encima de Nowitzki o Vince Carter en su draft.

     

Ya véis ¿os los imagináis juntos? Pues casi. Y es que Smush Parker, Luke Walton y Kwame Brown llegaron a jugar juntos en los Lakers dos temporadas. Y eso no es todo, cuando Brown fue traspasado a Memphis formó una terrorífica (para los aficionados memphitas) pareja interior con Milicic. ¿Os están dando ganas de ir a la videoteca a recuperar partidos de esos años? Te doy un adelanto, entra en este enlace y diviértete con el nivelón de la pareja Brown-Walton.

Trabajando con JSON en PHP: crear y consumir JSON

JSON se ha convertido en una herramienta fundamental en la programación web, ya sea en servicios web, para comunicar con apps de móviles, pasar datos de php a javascript…

Desde PHP podemos tanto crear un JSON como recibirlo y manejarlo. Para la creación debemos utilizar la función json_encode(), que recibe lo que queramos serializar (una cadena, una variable numérica, un array, un objeto) y devuelve una cadena con los datos serializados. Si le pasamos un array normal o una cadena, no veríamos mucho cambio tras la serialización, pero si le pasamos un array asociativo, tendríamos un resultado como el del ejemplo:

$miArray = array("ferrari"=>"rojo", "porsche"=>"plateado", "mercedes"=>"negro");
$string = json_encode($miArray);
//el valor de string sería {"ferrari":"rojo","porsche":"plateado","mercedes":"negro"} 

Pero la verdadera potencia la encontramos cuando la aplicamos a objetos, ya que JSON nos permite serializar cualquier objeto de PHP, con el mismo procedimiento que usamos arriba: la función json_encode.

Una cuestión a la hora de trabajar con JSON que debemos tener en cuenta es la codificación de caracteres. En principio json_encode() ya convierte a Unicode los resultados, ya que en la notación JSON usamos siempre Unicode. El problema es que PHP por defecto tratará la cadena de origen como UTF-8, así que si no estás trabajando con esta codificación, y estás usando, por ejemplo, ISO-8859-1, se tornará necesario que codifiques las cadenas a UTF-8 antes de convertirlas en JSON. Esto se hace con la función utf8_encode().

$miArray = array("palabro1"=>utf8_enconde("ñandú"), "palabro2"=>"maniqueo", "palabro3"=>utf8_encode("bandoneón");
$cadenajson = json_encode($miArray);

Con esto ya recibiríamos las cadenas codificadas en UTF-8 y prevendríamos errores de codificación.

Ok, con esto hemos visto como generar un JSON, pero ¿cómo lo consumimos?.

Bueno, primero habría que destacar que existen varias librerías para JSON en PHP, pero desde PHP 5.2 este incluye librerías nativas desarrolladas en C, que son las más recomendables por cuestiones de rendimiento. Ahora vamos a suponer un objeto tal como el del ejemplo:

$strjson = '{
   "elemento1": "valor1",
   "elemento2": "valor2",
   "elemento3": null,
   "otroobj": {
      "a ver": "oh",
      "venga": "va"
   };

Esa sería la cadena JSON que recibiría nuestro código PHP con un objeto serializado ¿cómo la transformamos en objeto? Pues si serializábamos con json_encode() el paso contrario será con json_decode():

$objetophp = json_decode($strjson);

//si imprimimos objetophp con print_r() nos dará:
stdClass Object
(
   [elemento1] => valor1
   [elemento2] => valor2
   [elemento3] =>
   [otroobj] => stdClass Object
   (
      [a ver] => oh
      [venga] => va
   )
) 

Al tener un objeto puedes acceder a sus propiedades como en cualquier objeto normal de PHP.

Una serie de avisos: Recuerda, como dijimos antes, que las cadenas deben estar en UTF-8, si usas otra codificación tendrás que tirar de utf8_encode. Los nombres tanto de las propiedades como de los valores deben ir entre comillas dobles, no se aceptan comillas simples y sólo los valores numéricos o null pueden ir sin comillas. No te dejes ni llaves ni corchetes abiertos, ni ninguna coma de más. Todo eso provocará fallos.

BuddyPress vs Drupal Commons: crea tu red social con software libre

Creo que alguna vez comenté por aquí que estoy trabajando en el desarrollo de módulos para la red social PHPFox. No es que me convenza mucho este cms para la creación de redes sociales, la verdad, pero es cosa de la empresa. En todo caso, creo que PHPFox tiene una documentación que en el mejor de los casos es chapucera y en el peor inexistente, una comunidad profundamente inactiva y mercantilizada y un grave problema de rendimiento y escalabilidad. Y además hay que pagar licencia.

Por eso dediqué unos días a buscar opciones libres para crear una red social, no porque a mi empresa le interese (que de momento parece que están muy contentos con Fox) sino por mera curiosidad, y tal vez para montar alguna algún día, pero es sólo una chorrada que me ronda la cabeza.

A lo que íbamos, ¿qué dos opciones libres suenan con más fuerza? Pues BuddyPress y Drupal Commons. Ambos no son un CMS propiamente dicho, sino extensiones para otros CMS que nos dan la funcionalidad de red social

BuddyPress, como te puedes imaginar, está desarrollado para WordPress. Su instalación es simple: instalas WordPress ( a poder ser, la última versión), subir el plugin al servidor e instalarlo desde el menú de instalar plugins. Total, que en 10 minutos tienes montado todo en tu servidor.

Drupal Commons, evidentemente, es un plugin para Drupal. Su instalación es todavía más sencilla: sólo debes seleccionar el perfil Drupal Commons durante la instalación de Drupal, y listo.

En cuanto a funcionalidades, BuddyPress es menos completo que Commons. El plugin de wordpress incluye un stream de actividades de los usuarios, perfiles extendidos, posibilidad de conectar con amigos, posibilidad de crear grupos extensibles, mensajes privados, foros de discusión y todas las fucionalidades de WordPress para crear blogs. Lo básico para crear una comunidad.

Commons por su parte viene de serie más completo: los perfiles de usuario incluyen más variables, es capaz de crear automáticamente grupos respecto a la información del perfil, incluye foros de discusión, blogs (eso sí, en este caso menos potentes que los de wordpress), creación y gestión de eventos, creación de wikis, administración de documentos, integración con RSS, noticias, mayor control de la privacidad, editor WYSIWYG para los textos, estadísticas de la comunidad

Bueno, parece que ahí gana Commons ¿o no? Piénsalo bien, más funcionalidades no significa mejor. No quiero decir que Commons por abarcar mucho apriete poco, no, simplemente que es posible que en tu comunidad no necesites todas las funcionalidades que Drupal Commons trae por defecto. Y que no vengan por defecto en BuddyPress no significa que no existan para este, sólo que las tienes que instalar aparte.

En cuanto a diseño, el que incluye por defecto BuddyPress me parece más atractivo, pero ambos pueden ser modificados y personalizados, ya sea a través de la edición manual del css, a través de simples menús de personalización o adquiriendo alguna plantilla (las hay gratis y de pago). En todo caso, para gente que no sepa CSS Drupal Commons es más cómodo de personalizar.

Al ser software libre ambos tienen una amplia comunidad por detrás: Commons está apoyado y mantenido por Acquia, los cuales además de permitirte la descarga gratuita del plugin también ofertan de forma comercial hosting y mantenimiento para tu red. Commons tiene un API que te permitirá desarrollar módulos para extender su funcionamiento, además de tener acceso a miles de módulos. Con BuddyPress tienes menos módulos específicos, pero con todo dispones de todos los módulos de Worpress para añadir.

No voy a decir que uno sea peor que el otro. Creo que para un usuario avanzado BuddyPress es más flexible, y para uno novato Commons, aunque más grande, puede ser más fácil de gestionar al no tener que andar instalando plugins. En todo caso, a vosotros corresponde decidir. Yo todavía tengo que hacer más testeos caseros con ambos, pero ya tengo un par de ideas rondando la cefa.

Dotcom está de vuelta: llega MEGA

Aunque Kim Dotcom no es un personaje al tenga especial cariño hay que destacar que la llegada de Mega es una de las noticias tecnológicas de 2013.

Y es que por mal que me caiga el tío también pienso que el cierre de Megaupload fue un atropello a la libertad para la distribución de contenidos on-line. Una guerra sobre el control de contenidos de la que ya he hablado y que no viene al caso en esta entrada.

Siguiendo con Mega, el primer día ha sido todo un éxito: 250000 usuarios en pocas horas, más de un millón registrados el primer día y los servidores casi colapsados. Y eso que muchos no tienen claro el funcionamiento del nuevo servicio. Y es que Mega no es Megaupload, el nuevo proyecto de Dotcom tiene más que ver con los servicios de almacenamiento cloud como Drive o Dropbox que con su antigua página. Usarla para compartir archivos de forma masiva se tornará más complejo, dado que toda la información estará encriptada, y sólo el usuario tendrá la contraseña. Con esto no sólo consigue dar mayor seguridad a su servicio, sino que también logra descargase de responsabilidades, ya que de esta forma Mega nunca sabe qué ha almacenado el usuario. Una inteligente jugada para evitar problemas con la ley estadounidense como los que tuvo Megaupload.

El problema es que para compartir material el que lo haga deberá publicar su contraseña. Todavía no me he hecho cuenta para probar el servicio, pero por lo que he leído ofrecen 50 GB para los usuarios con cuenta gratuita, por lo que parece una opción interesante para competir con los servicios de almacenamiento más populares.

Crear un web service RESTful con PHP

Ya que ayer hablaba de crear un web service con SOAP hoy toca complementar la información creando un web service REST. Esto nos permitirá crear lo que podríamos llamar una RESTful API.

En este caso nos vamos a servir del framework Slim y de la librería NotORM para el acceso a la base de datos.

Lo primero que haremos será crear una suerte de Hello World con Slim, para probar su funcionamiento

<?php
require "Slim/Slim.php";

// creamos una nueva instancia de Slim
$ejemplo = new Slim();

// agregamos una nueva ruta y un código
$ejemplo->get("/", function () {
    echo "<h1>Ejemplo</h1>";
});

// corremos la aplicación
$ejemplo->run();

Vale, ejemplo simplón de cómo usar Slim, más adelante veremos algo más. Ahora vamos con la conexión a la base de datos, en la que usaremos NotORM y PDO:

<?php
require "NotORM.php";

$pdo = new PDO($dsn, $username, $password);
$db = new NotORM($pdo);

Ok, conexión creada. Vamos a suponer que tenemos una base de datos con nombres de discos. La idea sería crear un webservice que nos devolviera todos los discos de la base de datos, codificados como un objeto JSON:

<?php

$app = new Slim(
    "MODE" => "developement",
    "TEMPLATES.PATH' => "./templates"
);

$app->get("/discs", function () use ($app, $db) {
    $discs = array();
    foreach ($db->discs() as $disc) {
        $discs[]  = array(
            "id" => $disc["id"],
            "title" => $disc["title"],
            "band" => $disc["band"],
            "genre" => $disc["genre"]
        );
    }
    $app->response()->header("Content-Type", "application/json");
    echo json_encode($discs);
});

Bueno, primero creamos una instancia de Slim, definiendo que la utilizaremos en un entorno de desarrollo y apuntando en qué carpeta están las plantillas de la maquetación. Verás que en el ejemplo hemos llamado al método get(), es uno de los métodos de Slim que te enruta a una petición GET en la url que se le envía como primer argumento. Como segundo argumento le enviamos una función callback que se ejecutará como respuesta. La palabra use nos permite acceder a las variables externas desde dentro del ámbito de aplicación de la función anónima.

Ahora un ejemplo donde se haría lo mismo pero pidiendo los datos de un disco concreto:

<?php
$app->get("/disc/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $db->discs()->where("id", $id);
    if ($data = $disc->fetch()) {
        echo json_encode(array(
            "id" => $data["id"],
            "title" => $data["title"],
            "band" => $data["band"],
            "genre" => $data["genre"]
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "No existe un disco con el id $id "
            ));
    }
});

A diferencia del anterior ves que en la ruta ahora enviamos la id del disco a buscar como parámetro.

Ok, esto nos permite insertar datos, pero ¿para insertar y modificar? Bueno, vamos a hacer un método para insertar los datos (mediante el método post() de Slim) y otro para actualizarlos (el método put()):

<?php

$app->post("/disc", function () use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $app->request()->post();
    $result = $db->discs->insert($disc);
    echo json_encode(array("id" => $result["id"]));
});

$app->put("/disc/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $db->discs()->where("id", $id);
    if ($disc->fetch()) {
        $put = $app->request()->put();
        $result = $disc->update($put);
        echo json_encode(array(
            "status" => (bool)$result,
            "message" => "El disco $id ha sido actualizado"
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "El disco $id no existe"
        ));
    }
});

El primer método nos devuelve el id del último disco insertado, el segundo nos devuelve un valor verdadero o falso y un mensaje de error u éxito. Supongamos que quisieras acceder a esto por medio de un formulario (aunque lo normal en una de estas API es hacerlo mediante el envío de datos por la URL). ¿Cómo diferenciarías si es una nueva inserción o una edición? Fácil, crea un campo oculto llamado _METHOD y dale el valor PUT, tal cual como en el ejemplo:

</pre>
<form class="hiddenSpellError">action="#" method="post"></form>
<pre>
   <input class="hiddenSpellError" type="text" />type="hidden" name="_METHOD" value="PUT"></pre>
Title: <input class="hiddenSpellError" type="text" />type="text" name="title">
<pre></pre>
Band: <input class="hiddenSpellError" type="text" />type="text" name="band">
<pre></pre>
Genre: <input class="hiddenSpellError" type="text" />type="text" name="genre">
<pre>

   <input class="hiddenSpellError" type="text" />type="submit" value="Submit">
</form>

Ok. Nos queda el borrar datos para tener todas las operaciones CRUD en nuestra RESTful API. Ahora nos serviremos del método delete() de Slim.

<?php

$app->delete("/disc/:id", function ($id) use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $db->discs()->where("id", $id);
    if ($disc->fetch()) {
        $result = $disc->delete();
        echo json_encode(array(
            "status" => true,
            "message" => "Disco borrado exitosamente"
        ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "El disco con id $id no existe"
        ));
    }
});

El framework Slim provee de muchas posiblidades a la hora de crear servicios web. Esto es una explicación básica de cómo se crearía cada método, pero las posibilidades son muy grandes. En su página de documentación puedes estudiar cómo sacar el 100% a este framework.

Crear un webservice básico con PHP y SOAP

SOAP es un protocolo de intercambio para servicios web basado en XML, sobre el que puedes leer en esta entrada de la wikipedia. Para no liarnos con preámbulos, vamos con la chicha ¿cómo creamos un servicio web en PHP?.

Para facilitarnos la vida empezaremos por descargar NuSOAP, un toolkit para el desarrollo de servicios web con SOAP en PHP, que nos proveerá de diversas clases para trabajar con este protocolo. Basta con descargarlo, descomprimirlo, meterlo dentro de nuestro proyecto y, cuando queramos usarlo, incluir nusoap.php como librería.

Ok, con NuSOAP instalado toca crear un servidor SOAP en nuestra aplicación. Para el ejemplo crearemos un servidor, lo llamaremos producto.php, que si recibe una petición donde se le pida una lista de libros devuelva tres títulos (es un ejemplo básico, piensa que en la realiad podrías acceder a una base de datos y dar muchas más funcionalidades).

<?php
	require_once "nusoap.php";
	 
        function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	            return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->register("getProd");
	$server->service($HTTP_RAW_POST_DATA);
?>

Ok, ahora necesitas un cliente, que llamaremos cliente.php. En el constructor, el cliente recibirá el url del servidor y para acceder al método que nos devuelve los libros recurriremos al método call(), al cual le pasaremos el nombre del método del servidor al que queremos acceder y los parámetros en forma de array. Además, también controlaremos que no haya errores en la comunicación.

<?php
	require_once "nusoap.php";
	$cliente = new nusoap_client("http://localhost/producto.php");
	 
	$error = $cliente->getError();
	if ($error) {
	    echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
	}
	 
	$result = $cliente->call("getProd", array("categoria" => "libros"));
	 
	if ($cliente->fault) {
	    echo "<h2>Fault</h2><pre>";
	    print_r($result);
	    echo "</pre>";
	}
	else {
	    $error = $cliente->getError();
	    if ($error) {
	        echo "<h2>Error</h2><pre>" . $error . "</pre>";
	    }
	    else {
	        echo "<h2>Libros</h2><pre>";
	        echo $result;
	        echo "</pre>";
	    }
	}
?>

Con esto ya tienes una idea múy básica del funcionamiento de un webservice SOAP construído con PHP. Pero claro, nos falta un archivo WSDL para tener un webservice decente. Aunque dicho archivo puede ser escrito a mano, NuSOAP puede generarlo por ti pasándole ciertos parámetros, por lo que lo ideal sería generarlo en el servidor. Así que modifica tu producto.php para que quede tal que así:

<?php
	require_once "nusoap.php";
	 
	function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	        return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->configureWSDL("producto", "urn:producto");
	 
	$server->register("getProd",
	    array("categoria" => "xsd:string"),
	    array("return" => "xsd:string"),
	    "urn:producto",
	    "urn:producto#getProd",
	    "rpc",
	    "encoded",
	    "Nos da una lista de productos de cada categoría");
	 
	$server->service($HTTP_RAW_POST_DATA);
?>

Como ves, el cambio en es cuando llamamos a register, ya que en vez de pasarle, como antes, el método en cuestión, le añadimos también varios argumentos para generar el WSDL:

  • El primer array nos permite definir el argumento de entrada y su tipo de datos
  • El segundo define la función de retorno y su tipo de datos
  • urn:producto es la definición del namespace
  • urn:producto#getProd es donde definimos la acción SOAP
  • Luego viene el tipo de llamada,que puede ser rpc, como en el ejemplo, o document
  • Tras esto definimos el valor del atribute use, que puede ser encoded o literal
  • Finalmente viene una descripción de qué hace el método al que llamamos

Ahora basta con que en el navegador accedas a producto.php?wsdl y verás el WSDL generado. Ya puedes copiarlo y añadirlo a tu directorio web (crea un archivo y llámalo, por ejemplo, libros.wsdl). Para que el cliente lo utilice debes modificar el código, y en el constructor, en vez del url le pasas el nombre del archivo, de forma que quede como en el ejemplo:

         $client = new nusoap_client("libros.wsdl", true);

Ahora sí, ya tienes montado un pequeño servicio web. El ejemplo es simplón, pero piensa en todas las funcionalidades que podrías incorporarle.

Danzig – Danzig

Corría el año 1987 cuando Glenn Danzig entraba en la nómina de Rick Rubin. Atrás quedaba el horror punk de Misfits y los trabajos más experimentales de Samhain. Rubin vió en el musculoso vocalista un potencial tremendo: poseía una presencia escénica indudable y una voz que parecía un cruce entre Elvis y Jim Morrison.

No pasaban entonces Black Sabbath por su mejor momento. La etapa Tony Martin, a pesar de ofrecer discos muy dignos, no acababa de reverdecer los laureles de los tiempos pretéritos en los que Ozzy o Dio se encargaban de las voces. La escena doom/sludge americana era extremadamente underground: Pentagram, Trouble, Saint Vitus, The Obsessed… apenas tenían presencia mediática. En Europa despegaba el speed metal germano mientras en EEUU todo era laca y luminosidad.

Danzig

Pero Rubin era un tío con cultura, un tío que sabía que las raíces del heavy metal estaban en Black Sabbath, estaban en gente como Blue Cheer, Pentagram o Grand Funk Railroad. Y sabía que los riffs marcados y pesados de ese protometal casarían perfectamente con la cavernosa voz del vocalista de New Jersey. Y tenía en el guitarrista John Christ y el bajista Eerie Von (que ya había militado en Samhain) a los perfectos obreros para construir esa música.

Con un cruce de crudo guitarreo blues con la influencia de los riffs de Tony Iommi nos llegó este primer disco de Danzig. Un disco que se abre con los hipnóticos armónicos de Twist of Cain, que conforman un riff sobrenatural, mágico, imponente. Un tema perfecto para abrir el disco, que sigue con los tintes épicos de Not of This World. She Rides por su parte es un tema lento, pesado, sudoroso, que se arrastra por tu piel. Soul on fire sigue con la línea pesada pero sugerente desgranada por la voz de Glenn. Am I Demon representa, en cambio, un disparo a nuestra cabeza, un cañonazo directo de puro rock and roll. Y entonces llega la joya de la corona, Mother, seguramente el tema más famoso que haya grabado Danzig. Tras el magistral single el disco decae un poco, aunque es complejo no decaer tras una joya de ese calibre: Possession probablemente es el tema más flojo del album, End of Time nos trae otro tema pesado y susurrante, The Hunter ahonda en las raíces más blueseras de la banda y cierra el disco una chirriante e hipnótica Evil Thing, un tema que nos devuelve a los mejores momentos del disco.

Glenn Danzig
Glenn Danzig, posando a lo Jim Morrison.

En los 90 reivindicar el legado de Black Sabbath se convirtió en lo más habitual entre las bandas metaleras de todo el planeta, sobre todo tras el paso del grunge, pero en aquel momento los riffs pesados y oscuros eran algo más propio del underground, y Danzing logró llevarlos a un mercado más mainstream de forma magistral, guiado por la sabia mente del productor Rick Rubin. Un disco básico en la historia del heavy metal, aunque a menudo muy olvidado, sobre todo en Europa.