jueves, 14 de julio de 2011
Parsear ficheros XML grandes
Disponemos de la extensión SimpleXML que, de forma muy sencilla, nos permite indicar un XML de entrada y lo tendremos cargado en un bonito y accesible objeto.
Sin embargo, esta solución no es viable cuando vamos a procesar XML de gran tamaño, ya que estaríamos cargando toda la información en memoria. Normalmente no será necesario, ya que lo que se suele hacer es recorrer el XML buscando alguna información concreta, o bien convertirlo a otro formato o cargarlo en nuestra base de datos.
Para este tipo de operaciones es mejor usar la extensión XMLReader. Su funcionamiento es parecido al de un cursor que va recorriendo el XML. Por tanto, al no parsearlo entero, el consumo de memoria es mucho menor.
Vistas estas dos alternativas (hay algunas más), a la hora de procesar un XML entero una buena solución puede ser combinar ambos métodos. Es decir, usar XMLReader para ir recorriendo los nodos e ir cargando cada uno de ellos con SimpleXML, para que el manejo de la información contenida sea trivial.
Sería algo así:
Fuente: http://stackoverflow.com/questions/1835177/how-to-use-xmlreader-in-php
jueves, 16 de junio de 2011
Cómo integrar reCAPTCHA en Wordpress
Últimamente estamos recibiendo mucho spam en los blogs de la plataforma. Y eso que tenemos instalado un plugin, WP-SpamFree, que parece que no está realizando del todo bien su trabajo.
Así que como primera medida preventiva, vamos a introducir un captcha en el formulario de envío de comentarios. La más popular de estas herramientas, adquirida y auspiciada hace tiempo por Google, es reCAPTCHA.
Supuestamente hay disponible un plugin para Wordpress. Y digo supuestamente porque el plugin parece haberse roto en las versiones 3.x de WP. Así que nuestro gozo en un pozo.
Tenemos, pues, tres opciones:
- Buscar otro plugin que permita añadir captchas a los formularios. La verdad es que no nos ha convencido ninguno.
- Arreglar el plugin de reCAPTCHA. Como contribución a la comunidad estaría genial, pero no tenemos los conocimientos ni la experiencia suficientes como para afrontar la tarea en un corto periodo de tiempo.
- Modificar nosotros el código de nuestro tema de Wordpress para añadir reCAPTCHA.
Al final hemos optado por la última opción. Una vez efectuadas las investigaciones pertinentes, no ha resultado demasiado complicado.
La implementación en el formulario es muy sencilla, siguiendo las instrucciones que podemos encontrar en su web: http://code.google.com/apis/recaptcha/docs/display.html.
Ahora sólo queda comprobar, a la hora de procesar el alta de comentarios, que el captcha introducido es correcto. Para ello, nuevamente tenemos dos opciones: escribir un plugin al uso o añadir código en la librería personalizada functions.php. Optamos una vez más por la segunda opción.
Lo que hemos hecho ha sido escribir una función que comprueba el captcha, a partir de las instrucciones que encontramos en la documentación: http://code.google.com/apis/recaptcha/docs/php.html. Y, haciendo uso de los filtros de Wordpress (tremendamente útiles para poder personalizar su comportamiento sin tocar el "core"), asignamos la función recién creada al filtro "pre_comment_approved", que se ejecuta antes de verificar si el comentario debe o no aprobarse.
// Custom reCAPTCHA "plugin"
add_filter('pre_comment_approved', 'recaptcha_solved_comment');
function recaptcha_solved_comment($approved)
{
require_once(dirname(__FILE__) . '/lib/recaptcha/recaptchalib.php');
$privatekey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$resp = recaptcha_check_answer($privatekey, $_SERVER['REMOTE_ADDR'], $_POST['recaptcha_challenge_field'], $_POST['recaptcha_response_field']);
if (!$resp->is_valid)
{
$approved = 'spam';
wp_die( __('Debes introducir el código de verificación correcto.') );
}
return $approved;
}
martes, 19 de abril de 2011
Cómo instalar Nginx y PHP-FPM en Ubuntu 10.10
En realidad, se trata de una tarea que, a día de hoy, es sencilla. Hace meses, no había repositorios oficiales para PHP-FPM, y había que andar compilándolo o bien usar repositorios alternativos. No obstante, desde la versión 10.10 de Ubuntu, PHP-FPM se encuentra en los repositorios oficiales. Sólo hay que hace algún pequeño ajuste en la configuración, que veremos a continuación.
En nuestros proyectos ya llevamos tiempo confiando en la dupla Nginx + PHP-FPM, con buenos resultados. Apache(2) pronto será cosa del pasado.
1. Instalamos Nginx:
apt-get install nginx
No será la última versión, pero nos valdrá. Si queremos la última (Nginx acaba de lanzar su versión 1.0) debemos instalarla desde la web oficial.
2. Instalamos PHP-FPM:
apt-get install php-fpm
PHP-FPM se ejecuta como servicio (demonio) escuchando por defecto el puerto 9000.
3. Configuramos Nginx para que envíe las peticiones a PHP-FPM. Ésta es la parte más "complicada" del invento. En el fichero /etc/nginx/nginx.conf descomentamos las líneas correspondientes:
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
include fastcgi_params;
}
Donde /var/www es el directorio raíz del servidor web, y tenemos que tener cuidado en separar las palabras include fastcgi_params, que en el fichero de configuración por defecto vienen juntas por error.
Otro cambio que puede ser útil, si queremos que el servidor web responda a varios dominios sin redirigir al primero de ellos (que es el comportamiento por defecto), consiste en añadir a la configuración el parámetro server_name_in_redirect off.
4. Reiniciamos NGinx:
/etc/init.d/nginx restart
5. Añadimos extensiones a PHP. Aquí, a gusto del cosumidor. Por ejemplo:
apt-get install php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-json
Si añadimos extensiones, debemos reiniciar PHP-FPM:
/etc/init.d/php-fpm restart
Y ya está. Si creamos un fichero info.php con el código:
<?php
phpinfo();
?>
Y accedemos desde nuestro navegador, deberíamos ver las opciones de PHP que hemos instalado.
Fuente principal: http://www.howtoforge.com/installing-nginx-with-php5-and-php-fpm-and-mysql-support-on-ubuntu-10.10.
martes, 15 de febrero de 2011
KSES – Filtrado sencillo de etiquetas HTML
Finalizando el proyecto de migración de los blogs a WordPress, ya en la fase de prueba de funcionalidades, me di cuenta de que, al dar de alta una entrada, WordPress permite por defecto muy pocas etiquetas HTML. Así que tenía la necesidad de permitir al menos las que incrustan vídeo, ya que son muy usadas.
WordPress usa TinyMCE en la parte cliente como editor “Rich-text”. ¿Pero en servidor, cómo hace la validación?
Pues lo hace mediante la librería KSES.
KSES se configura de forma muy parecida a TinyMCE. En un array definimos cuáles son las etiquetas permitidas, los atributos de dichas etiquetas e, incluso, los valores permitidos para dichos atributos. De esta forma, permite un filtrado mucho más potente que la función strip_tags de PHP.
Al final he optado por usar el plugin Extend KSES, que implementa la librería KSES y proporciona toda la funcionalidad que necesitaba.
Parseando HTML
Lo ideal, cuando necesitamos obtener datos de un proveedor, es acceder a un servicio web que nos los proporcione en algún formato estructurado (XML, JSON o similar). Lamentablemente, no siempre disponemos de este servicio, pero sí que es posible que podamos obtener la información en alguna página web, en formato HTML.
Para ello, lo que tenemos que hacer es interpretar el HTML, extrayendo los datos que nos interesen. En nuestra ayuda viene una librería como PHP Simple HTML DOM, que nos permite realizar esta función de manera sencilla, empleando selectores como los de jQuery.
La pega (que no debería ser tal): sólo funciona con PHP 5 en adelante.
Por descontado, nuestro parser estará íntimamente ligado al código HTML de la página. Cualquier mínimo cambio puede dejar nuestro script inutilizable. Pero al construirlo con esta librería, como las reglas de selección son bastante claras, es fácil adaptarlo.
Generar la salida de error en un script PHP

La salida de un script PHP lo normal es que sea en la salida estándar, que es donde se vuelca la información para que sea recogida por el cliente de turno. Esto suele hacerse mediante las sentencias echo y print que siempre imprimen en la salida estándar, pero ¿y si quiero enviar un mensaje a la salida de error?.
Cuando programamos nuestros scripts de linea de comandos surge la cuestión de necesitar trabajar con el resto de canales de entrada y salida que define el sistema. De los canales definidos en el sistema como salida tenemos dos, la salida estándar (stdout) y la salida de errores (stderr). Son dos canales distintos por donde se puede volcar la salida de un script, y por lo tanto nos puede interesar poder especificar porque canal quiero mandar la información. Lógicamente la salida estándar será para los mensajes de proceso y la de error para mensajes que indiquen un comportamiento anómalo al proceso.
Para esto PHP define una serie de secuencias de entrada y salida para poder utilizar con las funciones de tratamiento de ficheros, y que nos permite tratar información sobre los canales del sistema. Sobre los canales de salida que nos interesan están php://stdout y php://stderr que son los que ofrecen acceso al canal de salida estándar y de error respectivamente. Estos canales están definidos en unas constantes (STDOUT y STDERR) que definen el descriptor de cada canal, y que permiten trabajar con ellos usando directamente las funciones de escritura…
fwrite(STDOUT, "Conectando a servidor");
fwrite(STDERR, "ERROR: No se pudo acceder al recurso");
Al utilizar la constante STDERR como descriptor de fichero en la función de escritura, todos los mensajes que se escriban saldrán por el canal de error.
Así en nuestro scripts podremos diferenciar este canal…
php -q script.php 2> /var/log/script/script_php.error.log
Diferencias entre entornos
Para ejecutar un script de PHP podemos tener el intérprete compilado de dos formas diferentes: CGI y CLI
CLI
PHP actúa como si fuera un script de linea de comando. Es la versión que se tiene en el entorno de desarrollo, y sería la opción más adecuada para lanzar los scripts que se programan como tareas en el sistema.
No tiene asociada ninguna excepción sobre lo que hemos visto.
CGI
Integrado en los entornos de test y producción.
Usado antiguamente (y puede que actualmente) como el interprete que se configuraba en los servidores web para poder integrar PHP. Esta versión esta más orientada a ejecutar scripts en un entorno web, y no desde linea de comando como suelen ser nuestro caso. Es por esto que existen diferencias con la versión CLI y en este caso con los descriptores de ficheros.
No existen las constantes que representan los canales de salida STDOUT y STDERR, y por lo tanto hay que realizar la escritura de otra manera…
$fd_err = fopen("php://stderr","w"):
fwrite($fd_err, "ERROR: No se pudo acceder al recurso");
… para poder trabajar con la secuencia de error.
Solución
Para no tener que escribir código diferente entre las versiones se puede definir las constantes para que el trabajo sea igual y quede como si no hubiera diferencia…
if(!defined('STDERR')) define('STDERR', fopen("php://stderr","w"));
fwrite(STDERR, "ERROR: No se pudo acceder al recurso");
… así, si la constante no está definida se creara, y nuestro código quedaría igual para los diferentes entornos.