Category Archives: PHP

Figlet as a Service

Todo comenzó cuando quise poner un ASCII Art a las máquinas para el fichero /etc/motd. Para hacerlo quería usar un playbook de Ansible que me permitiera generar el código ASCII utilizando como entrada en nombre del host.

Me puse a investigar y a sopesar las opciones:

  1. Podía instalar figlet en cada máquina y generar el ASCII Art.
  2. Podía usar alguna API que proporcionara algún método para obtener los ASCII Art en remoto

La primera opción no me convence porque tengo que instalar un paquete que solo se va a usar una vez y la segunda menos porque las opciones que encontré eran de pago y algunas máquinas no tienen salida a Internet.

Así pensé utilizar una combinación de ambas y crearme mi propio servicio de figlet. No ha sido demasiado complicado crearme un par de contenedores docker que para ofrecer el servicio a la infraestructura.

Dockerizando Figlet

Lo primero que hemos hecho ha sido buscar en Docker Hub el contenedor para PHP que ejecute FPM. Delante de este contenedor colocamos otro contenedor NGINX que actúa como frontend de la aplicación.

Esto no es complicado, porque ya está hecho, el hub oficial de PHP tiene un tag para FPM. A este contenedor hay que instalarle el paquete de figlet. Con apt se hace. Se escribe este cambio en el Dockerfile y se construye la imagen.

El contenedor de NGINX hay que configurarlo para que haga de proxy al php-fpm

Escribimos el código para leer la URL de la que sacaremos el nombre del host. Tenemos en cuenta si viene vacía, o si es mayor a 32 caracteres, ya que la RFC considera que el tamaño máximo de nombre de host es de 32 caracteres. También para este caso se pasa un ancho de columna a figlet de 160 caracteres, para que no salga cortado, en cualquier caso es raro que un nombre de host sobrepase los 15 caracteres, al menos en las infraestructuras que yo administro.

Construida la imagen, se escribe el docker-compose.yaml para vincular el nginx con el php-fpm. Se establece la política de restart, se exponen los puertos y se habilitan los volumenes.

Publicado

Está publicado aquí

Fuentes

Y las fuentes en mi github por si alguien quiere investigar o montarlo en su infraestructura.

Leer XML desde PHP

Uno de los problemas que más me ha costado resolver ha sido leer un XML desde cualquier lenguaje de programación, hoy voy a presentar cómo he resulto el problema utilizando el DOM (Document Object Model, Modelo de objetos del documento). Esto según la Wikipedia es:

es esencialmente una interfaz de programación de aplicaciones (API) que proporciona un conjunto estándar de objetos para representar documentos HTML y XML, un modelo estándar sobre cómo pueden combinarse dichos objetos, y una interfaz estándar para acceder a ellos y manipularlos.

Para ello partimos de un XML con este aspecto:

<?xml version="1.0" encoding="UTF-8"?>
<coleccion>
  <peli>
    <titulo>El señor de los anillos</titulo>
    <director>Peter Jackson</director>
    <anio>2000</anio>
  </peli>
  <peli>
    <titulo>La jungla de cristal</titulo>
    <director>John McTiernan</director>
    <anio>1988</anio>
  </peli>
  <peli>
    <titulo>Rocky</titulo>
    <director>John G. Avildsen</director>
    <anio>1976</anio>
  </peli>
</coleccion>

Es la pequeña colección de películas, para leer el XML hacemos uso de la siguiente pieza de código en PHP

<?php
  $objDOM = new DOMDocument(); 
  $objDOM->load("pelis.xml"); // Cargar el fichero XML

  $entrada = $objDOM->getElementsByTagName("peli"); 
  // Por cada peli cogemos los valores de los campos

  foreach ($entrada as $value) { 
    $t = $value->getElementsByTagName("titulo"); 
    $Titulos[] = $t->item(0)->nodeValue;
    $d = $value->getElementsByTagName("director"); 
    $Directores[] = $d->item(0)->nodeValue; 
    $a = $value->getElementsByTagName("anio"); 
    $Anios[] = $a->item(0)->nodeValue; 
  } 
    // Mostramos los resultados
    $tam = count ($Titulos);
    for ($i = 0; $i < $tam; $i++) 
      echo $Titulos[$i].' por '.$Directores[$i].' en el año '.$Anios[$i].'
'; ?>

El resultamos lo vemos a continuación:

El señor de los anillos por Peter Jackson en el año 2000
La jungla de cristal por John McTiernan en el año 1988
Rocky por John G. Avildsen en el año 1976

Añadiendo marcas de agua al vuelo para evitar hotlinking

A partir de una entrada que aparecía esta mañana en Tu Receta, he pasado el día intentando mejorar el sistema. Ellos proponen que cuando alguien enlace directamente a una foto de tu sitio, cambiar la url mediante mod_rewrite hacía otra imagen para que en el sitio sólo pueda mostrar una imagen con algún mensaje o similar, pero no las imágenes originales. Recordé que mi amigo Migue había pasado por eso y lo resolvió, así que empece a investigar para ver cómo podría solucionarlo.

Requerimientos:

  1. Tener activado mod_rewrite en nuestro servidor
  2. PHP5-GD para trabajar con imágenes
  3. Una imagen que será la marca de agua

Lo primero que hacemos es añadir a nuestro .htaccess las nuevas reglas para mod_rewrite que son las básicamente las mismas que en Tu Receta pero cambiamos la RewriteRule así:

RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://www.muspells.net/.*$      [NC]
RewriteCond %{HTTP_REFERER} !^http://www.muspells.net$      [NC]
RewriteRule ^(.*)\.(gif|jpg|png)$ image_script.php?image=$1\.$2   [R]

Cambiamos la regla de reescritura para que apunte a un script de PHP donde editaremos al vuelo la imagen para devolverla al REFERER con una marca de agua.

Hemos llamado al script image_script y lo hemos colocado en la raíz de documentos,

<?php

// En WordPress por ejemplo las imagenes se guardan en directorios,
// necesitamos el PATH completo a la imagen.
$URI = $_GET['image'];
$URI_PARSER = explode ('/', $URI);
$image = "/path/to/DocumentRoot/".$URI_PARSER[count($URI_PARSER) - 1];

switch (TRUE) {
   case stristr($image,'jpg'):
      $photoImage = ImageCreateFromJpeg("$image");
      break;
   case stristr($image,'gif'):
      $photoImage = ImageCreateFromGIF("$image");
      break;
   case stristr($image,'png'):
      $photoImage = ImageCreateFromPNG("$image");
      break;
}

ImageAlphaBlending($photoImage, true); 

// Añadimos aquí el fichero de marca de agua.
$logoImage = ImageCreateFromPNG("watermark.png"); 
$logoW = ImageSX($logoImage); 
$logoH = ImageSY($logoImage); 

// Los 1's representan el margen con el margen superior izquierdo
ImageCopy($photoImage, $logoImage, 1, 1, 0, 0, $logoW, $logoH); 

ImagePNG($photoImage); // output to browser 

ImageDestroy($photoImage); 
ImageDestroy($logoImage); 
?>

El script colocará la imagen watermark.png en la esquina superior izquierda.

Cuando accedemos a alguna imagen de nuestro sitio de forma legítima la veremos así:

gnu

GNU

Mientras que si accedemos enlazando directamente la veremos así:

gnu

Fuentes:

  1. Tu Receta: Evitar Hot-linking con .htaccess
  2. El script

“Tumbleando” Desde La Consola

Me he hecho una cuenta de Tumblr y quería ver si se podía postear desde la línea de comandos que es donde estoy más tiempo. Así que leí la sencilla API que tienen y me puse a programar basando en el ejemplo que tienen publicado. Bueno, al principio quería hacerlo en Python pero se me hacía demasiado complejo manejar una petición POST con multipart/form-data. Así que tomé el ejemplo y lo completé para poder enviar fotos y vídeos en esta primera versión. En el futuro, quiero que el script reconozca que estoy posteando y lo pueda clasificar sólo. Así espero tener soporte para más cosas. ¿Que por qué hago esto? porque no puedo hacer las cosas como un usuario normal… es lo que hay.

<?php
$tumblr_email = 'direccion de correo';
$tumblr_password = 'secreto';

// Control de la linea de parametros
if (count($argv) < 4) {
  exit("ERROR: Uso: $argv[0] [photo | video] url caption\n");
}

// Datos de la entrada
$post_type = $argv[1];
if ($post_type != "photo" && $post_type != "video") {
  exit("Error: El primer parametro debe ser photo o video\nHa escrito $argv[1] en $post_type\n");
}
$post_embed   = $argv[2];
validateURL($post_embed) or exit ("Error: no ha introducido una URL válida\n");
$post_source  = $argv[2];
validateURL($post_source) or exit ("Error: no ha introducido una URL válida\n");

$post_caption = $argv[3];

// Preparación de la peticion POST
if ($post_type == 'photo') {
  $request_data = array(
        'email'     => $tumblr_email,
        'password'  => $tumblr_password,
        'type'      => $post_type,
        'generator' => 'PHP-Cli-Tumblr 0.1 http://www.muspells.net',
        'source'    => $post_source,
	'caption'   => $post_caption
    );
} else {
  $request_data = array(
        'email'     => $tumblr_email,
        'password'  => $tumblr_password,
        'type'      => $post_type,
        'generator' => 'PHP-Cli-Tumblr 0.1 http://www.muspells.net',
      	'embed'	    => $post_embed,
	'caption'   => $post_caption
    );
}

// Enviar la petición POST (con cURL)
$c = curl_init('http://www.tumblr.com/api/write');
curl_setopt($c, CURLOPT_COOKIEJAR, "my_cookies.txt");
curl_setopt($c, CURLOPT_COOKIEFILE, "my_cookies.txt");
curl_setopt($c, CURLOPT_FOLLOWLOCATION,1);
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $request_data);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($c);
$status = curl_getinfo($c, CURLINFO_HTTP_CODE); 
curl_close($c);

// Todo bien?
if ($status == 201) {
    echo "Success! The new post ID is $result.\n";
} else if ($status == 403) {
    echo "Bad email or password\n";
} else if ($status == 400) {
    echo "Bad request\n";
} else {
    echo "Error: $result\n";
}

function validateURL($url) {
  if($url==NULL) return false; 
	$protocol = '(http://|https://)'; 
	$allowed = '([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)'; 
	$regex = "^". $protocol . // must include the protocol 
			 '(' . $allowed . '{1,63}\.)+'. // 1 or several sub domains with a max of 63 chars 
			 '[a-z]' . '{2,6}'; // followed by a TLD 
	return eregi($regex, $url);
}

?>

Para lanzar el script, simplemente

$ php toTumblr.php [photo | video] url comentario

He tratado de controlar un poco la línea de comandos sobretodo, porque aquí el orden de los parámetros si influye. En el futuro, espero que no sea necesario.

La función que verifica que la url es válida es de la documentación de PHP.