Archivo de la categoría: Programacion

Sobre programacion en general … de lo poco que tengo lo comparto por aqui

[PostgreSQL] Crear triggers dinámicos con postgreSQL

¿Qué es un trigger dinámico?, es aquel trigger que la metadata de en cual tabla/campo va a operar es pasado de manera dinámica (parámetros) a la función de trigger (trigger function).

¿Y para qué sirve?, bueno, imaginen un sistema dónde cada operación debe ser por ejemplo, agregada a una tabla auditoría, o por ejemplo, que una operación en una tabla, causa una operación en otra tabla, que aunque el código sea *casi* el mismo, depende de la tabla que dispara el trigger, qué tabla va a operar.

La utilidad de esto es simplemente poder re-utilizar una única funcion trigger para diversas acciones a ser tomadas de acuerdo a los parámetros recibidos.

El ejemplo

Este ejemplo se construyó durante un taller de postgreSQL que estaba dictando, el criterio era el siguiente:

  • El trigger toma el valor insertado en una tabla
  • Busca dicho valor en otra tabla, usando otro campo como criterio
  • Si el valor no existe, retorna NULL
  • Si el valor existe, retorna el objeto NEW del disparador, con lo cual la inserción sí ocurre

Los campos TG_*

En los disparadores de postgreSQL se cuenta con un conjunto de variables muy útiles, como son:

  • TG_OP: define la operación realizada en la tabla (INSERT, UPDATE o DELETE)
  • TG_TABLE_SCHEMA: define el schema al que pertenece la tabla (ej: “public”)
  • TG_TABLE_NAME: define el nombre de la tabla que disparó este trigger
  • TG_ARGV: es un arreglo (array []) que contiene los parámetros enviados a una función de trigger.

Con esto podemos hacer cualquier tipo de condicional en la función de trigger, reduciendo el código a sólo una función que se usará, de acuerdo a los parámetros que se le pasen.

La Función

CREATE OR REPLACE FUNCTION permitir_insert() RETURNS trigger AS

Comenzamos con la definición, la llamaremos “permitir_insert()” y obviamente, retorna un trigger.

luego, en la sección de declaración, definimos las únicas variables que vamos a necesitar para este ejemplo:

 vrecord integer;
campo text;
tabla text;
id int;
rc int;

BEGIN

En el cuerpo de la función, preguntamos qué tabla y campo voy a consultar para verificar si permito o no el insert, esto se hace consultando a TG_ARGV:

 tabla := TG_ARGV[0];
campo := TG_ARGV[1];

Como ven, ya que podamos recibir cualquier cantidad de parámetros en el trigger, las opciones son infinitas.

Ahora, necesito saber que hay en el campo trigger (NEW.campo), en este caso, debo reemplazar “campo” con el nombre del campo de la tabla que disparó el trigger (y que voy a consultar en otra tabla), como en mi caso el valor de el “campo” en todas las “tablas” que usarán esta función de trigger es entero, he declarado “vrecord” integer, si su tipo será distinto, sería conveniente declararlo “anyelement”.

EXECUTE 'SELECT ($1).' || quote_ident(campo) || '::text' INTO vrecord USING NEW;

Lo que hace ese código, es ejecutar un $1.”campo”::text (dónde campo es una variable) y mete su valor en la variable “vrecord”, se usa el objeto NEW para la sustitución del $1.

Ejemplo, si el campo se llama “piezas“, ese “EXECUTE” quedaría “NEW.piezas” y entonces metería el valor de NEW.piezas en la variable “vrecord”.

¿sencillo, no?.

La consulta

Ahora, debo verificar que en el campo “campo”::text de la tabla tabla::text (variable) existe un valor con lo que está contenido en “vrecord”, de lo contrario retorno NULL y el trigger no se ejecuta (la operación de inserción no ocurre).

Sería algo como un “constraint” pero sin usar reglas de constraint, ¿entendido?.

IF TG_OP = 'INSERT' THEN

EXECUTE format('SELECT '||quote_ident(campo)||' FROM '||quote_ident(tabla)||' WHERE '||quote_ident(campo)||' =$1') USING vrecord INTO id;

En este caso, he ejecutado un SELECT (SELECT “campo”::text FROM “tabla”::text WHERE campo::text = vrecord), claro que haciendo las conversiones y los reemplazos respectivos.

El valor de esa ejecución lo agrego a la variable id.

Si adicionalmente se desea averiguar si esa consulta anterior retornó filas, colocamos seguidamente al EXECUTE:

GET DIAGNOSTICS rc = ROW_COUNT;

Si “rc” es igual a cero, entonces no existe el valor “vrecord” en el campo “campo” de la tabla “tabla”, caso contrario, se retorna NEW.

 IF rc = 0 THEN
    RAISE EXCEPTION 'no existe el valor % en el campo % de la tabla %', vrecord, campo, tabla;
    RETURN NULL;
END IF;
RETURN NEW;

Y listo!, definimos el cierre y que esta es una función VOLATILE:

END;
LANGUAGE plpgsql VOLATILE

Y ya podemos usarla.

Usando la función dinámica

Para usar la función dinámica, simplemente creamos un trigger en cada tabla que necesite convocarla, pasando como parámetros de la función trigger la tabla referencia y el campo que debe evaluar, ejemplo:

CREATE TRIGGER trg_insert_detalle_reportes
BEFORE INSERT
ON reportes
FOR EACH ROW
EXECUTE PROCEDURE permitir_insert('reportes', 'id_reporte');

Se insertará un detalle de reporte, solamente si el valor de “id_reporte” aparece en la tabla “reportes”.

Conclusiones

Sé que parece muy “rebuscado” un ejemplo que bien podría salir con una clave foránea, pero sirve para el hecho de demostrar la posibilidad de obtener e iterar sobre el objeto NEW, consultar metadata al “information_schema” o realizar cualquier operación de manera dinámica, pasando parámetros y consultando las variables mágicas TG_* de postgreSQL.

¿Se les ocurre alguna idea para estos triggers dinámicos? …

[Linux] LXC: La “virtualización” barata

¿Qué es LXC?

LinuX Containers, una tecnología de contenedores que permite crear jaulas aisladas, una jaula contenedora es un sistema GNU/Linux auto-contenido dentro de otro, al que se le pueden aplicar cuotas de disco, CPU, memoria y algunos límites y capacidades, asignarle una (o varias) interfaces de red e “iniciarlo” como si fuera un equipo independiente.

LXC es construido encima de una jaula (chroot) y proporciona un sistema virtual completo, con mecanismos de “aislamiento e individualización” (isolation) que son completamente nativos al kernel Linux.

A diferencia de una virtualización al estilo de Xen, una “virtualización” basada en contenedores solamente puedes levantar el sistema operativo del host (en este caso, GNU/Linux), sin embargo, se puede levantar diversas “versiones” de GNU/Linux, al estilo de openVZ, con LXC se pueden levantar contenedores “compatibles” en Linux basados en Fedora, CentOS o Ubuntu y a diferencia de openVZ,  no requieren un micro-kernel o un parche completo al kernel Linux, ya que se basa exclusivamente en una tecnología de espacios y grupos (CGROUPS) que es nativa del kernel Linux desde el 2.6.30 y cualquier equipo con un kernel 2.6.30 (o superior), CGROUPS habilitado y los scripts de LXC puede comenzar a crear contenedores.

Se conoce como una “virtualización barata” puesto que no se requiere mucho recurso para levantar un contenedor LXC, consumen mucho menos que una maquina completa Xen y un poco menos que openVZ puesto que LXC se basa en un “chroot aislado y mejorado”, se pueden levantar docenas de “mini-máquinas virtuales” en un simple host.

¿Para qué usaríamos LXC?

A diferencia de openVZ, LXC no requiere un kernel “parcheado” (incluso un kernel Xen puede levantar recursos LXC), sin embargo, LXC no es una tecnología muy “completa”, por ejemplo, a diferencia de openVZ, no se pueden limitar capacidades de manera granular y específica y el kernel “oficial” de Debian estable (Squeeze 6.02) no posee por ejemplo habilitado el control de memoria (cgroup.memory).
Aún asi, LXC es muy útil para iniciar servicios aislados que anteriormente utilizabas una jaula chroot (como DNS) o aquellos que por su bajo consumo de recursos podrían correr perfectamente sobre una jaula LXC, algunos ejemplos serían: DNS, DHCP, Kerberos, Entidad Certificadora, pero que ahora, teniendo su propia reserva de recursos y su propia IP, podrás correrlo de manera aislada y segura.

¿Qué necesitamos?

LXC corre muy bien en Debian Squeeze estable, incluso sobre el kernel Xen 2.6.32, sin embargo, las herramientas para crear “contenedores” están bastante “desfasadas”, yo me hice mi propio script bash que crea un contenedor LXC de Debian usando debootstrap, tengo ya algo más de año y medio con ese script y lo pongo a disposición de quien lo necesite.

Si deseas “experimentar”, tendrás que saber que los núcleos 3.01 y 3.02 fallaron en su configuración de CGROUPS namespaces para soportar claramente LXC (Debian bug) y si desean utilizarlo conjuntamente con Xen, tendrán que subir hasta la última versión en backports, o sea:

  • * linux-image-3.2.0-0.bpo.1-rt-amd64
  • * cgroups habilitado
  • * xen-linux-system-3.2.0-0.bpo.1-rt-amd64
  • * lxc 0.7.5-24

Sin embargo, luego de algunas pruebas, he notado que ambos (Xen 4.1 sobre Kernel 3.2) y LXC (versión 7.4 en adelante) pueden coexistir pacíficamente:

xm list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  2985     4     r—–    541.9
ldap                                         1   256     1     -b—-     24.1

Y en el mismo equipo, LXC:

lxc-ls
cache
vm1

LXC-Tools: creando contenedores

LXC incorpora algunas “plantillas” para crear contenedores, pero están bastante desatendidas (aún en Wheezy, se consiguen plantillas solamente para crear VMs en Debian Lenny), por lo que hemos creado una serie de scripts que permiten crear contenedores para Debian Squeeze o Wheezy sobre un servidor con CGROUPS y LXC < 0.7.4.

Con LXC > 7.4 lxc-create incorpora la posibilidad de crear contenedores Debian incluso sobre Debian Sid, del que hablaremos en el siguiente capítulo.

Hay que tomar en cuenta que las cosas no se quedan ahí, ya que podemos crear contenedores para cualquier versión de GNU/Linux Debian-Based utilizando debootstrap (Ubuntu, Canaima) y podemos crear Fedora o CentOS utilizando rpmstrap, pero a su vez, una template para openVZ/proxmox (con algunos cambios) es compatible con una VPS LXC, por lo que veremos algunos avances en los scripts de lxc-tools.

Instalando lxc-tools

Tome nota que para poder utilizar contenedores LXC debería cumplir algunas cosas:

* Montado los CGROUPS (tip: agregar esta línea en el archivo /etc/fstab “none /sys/fs/cgroup cgroup defaults 0 0”)
* Instalado paquete LXC
* La forma más fácil de configurar red, es a través de un bridge, debe existir uno configurado (vea: mi artículo sobre virtualización)
* La interfaz bridge debe estar activa (revisar: “brctl show”)

Instalamos las dependencias del script:

apt-get install lxc lsb-release

Descarguen lxc-tools desde su fuente en Gitorious:

git clone https://git.gitorious.org/lxc-tools/bash_version.git

Cambie el modo a ejecutable al script principal:

chmod +x lxc-tool

y ya estamos listos para utilizarlo!.

Usando lxc-tools

lxc-tools viene con un archivo de configuración que permite definir ciertas opciones del script como por ejemplo el tamaño del contenedor, la versión de GNU/Linux (default: Debian), la Suite (default: squeeze) y otras opciones como el grupo de volumen donde crear la VM (TODO: en próxima entrega crearé contenedores directamente en un directorio); sin embargo, el script tratará de auto-descubrir la mayoría de las opciones necesarias.

Crear una VM es tan fácil como ejecutar:

lxc-tool create -n nombre

Este hará las preguntas necesarias y las otras las tomará del archivo de configuración o las autodetectará.

Notas: lxc-tools son unos scripts que tuve más de un año desatendidos, por unas necesidades inmediatas, hice algunos cambios y estaré agregando otros (como la creación de VMs a partir de plantillas de proxmox u openvz), sin embargo, la template para Debian incorporada en LXC 0.7.4 es realmente superior!.

Usando LXC

El comando más básico que deben conocer es “lxc-checkconfig”, este permite verificar si la configuración de nuestro Kernel Linux corresponde a las necesidades de LXC:

lxc-checkconfig

Verán una salida semejante a esta:

Kernel config /proc/config.gz not found, looking in other places…
Found kernel config file /boot/config-3.2.0-1-amd64
— Namespaces —
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled

— Control groups —
Cgroup: enabled
Cgroup namespace: required
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled

— Misc —
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled

Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig

Si CGROUPS está activo y el kernel soporta LXC, al instalar LXC podremos comenzar a crear VMs.

* Creando una VM:

lxc-create -n nombre -t [debconf|ubuntu|debian]

Donde:

-n : nombre que le daremos al conenedor

-t : template de construcción (debconf, debian base o ubuntu)

* Iniciando una VM:

lxc-start -n nombre

* Deteniendo una VM:

lxc-stop -n nombre

* Listando las VMs activas y creadas:

lxc-ls

Si desean una guía completa de LXC, revisen acá http://lxc.teegra.net/

Conclusiones

LXC se levanta como una alternativa fácil (de gestionar, de trabajar, de configurar, de mantener) a otros sistemas de virtualización/contenedores) su incorporación al ecosistema LIBVIRT nos permite administrarlo fácilmente con cualquier herramienta diseñada para usar libvirt (ej. virt-manager) y su bajo consumo de recursos permite ser pensado para virtualizar en entornos donde otras tecnologías serían imposibles de lograr (ejemplo: ¿se imaginan virtualizar contenedores LXC dentro de una Tablet Android para paralelizar tareas?).

TODO: ¿sería posible crear contenedores Fedora/CentOS desde cero si incorporamos rpmstrap a la fórmula?, ¿será posible incorporar LXC a un sistema Dual-Core Nvidia-Tegra 2 de una tablet?

Prueben y opinen sobre LXC, y como siempre:

Happy Hacking!

Rotación y Auto-Rotación de pantalla (iphone-like) con Linux

La rotación de pantalla es una posibilidad nativa del sistema X, basado claro está en xrandr (un comando de X que permite rotar la pantalla, cambiar resolución, etc), ahora bien, la idea de asociar esto a una tecla o mejor aún, aprovechar las capacidades de nuestro hardware para hacerlo de manera automática es mucho más práctico.

El módulo thinkpad_acpi

En mi caso, al tener una portátil Thinkpad, uso el módulo thinkpad_acpi para controlar los “botones especiales” de la portátil, para “asegurar” la carga de este módulo hemos agregado su nombre a:

/etc/modules

Y listo!, podemos usar el botón thinkVantage, subir y bajar volumen, cambiar de escritorio, etc.

Ahora bien, la Thinkpad tablet PC viene con un botón que se usa “precisamente” en Windows para rotar la pantalla, su combinación de pantalla es “0xc7” y puede ser asociado fácilmente a cualquier cosa, en la sección SISTEMA > “Preferencias” > “Combinaciones de Teclas” de Gnome, para ello cargamos el selector de combinación de teclas, y presionamos el botón “Añadir”:

Creamos un acceso llamado “Rotar Pantalla” y lo asociamos a un script en python llamado /usr/bin/rotate-screen.py que ya veremos como funciona.

La cosa queda así:

Ahora, a trabajar en el script!.

El Script de rotar la pantalla

El guión de rotar pantalla es un simple script de python, que usa xrandr para rotar la pantalla y usa xsetwacom para rotar el cursor/stylus/puck de la tablet PC, adicionalmente utiliza el módulo HDAPS (Hard Disk Active Protection System) para determinar con el acelerómetro la posición actual (si es izquierda, derecha, normal o invertida), para los que no tengan acelerómetro en el disco duro, simplemente podrían determinar el actual modo de rotación y cambiar al siguiente (para hacer una rueda infinita de rotación normal > izquierda > inverted > derecha).

Es como ejecutar “a mano” los siguientes comandos:

normal=’xrandr –output LVDS –rotate normal’
left=’xrandr –output LVDS –rotate left’
right=’xrandr –output LVDS –rotate right’
inverted=’xrandr –output LVDS –rotate inverted’

También usa el módulo Thinkpad-ACPI para determinar el modo en el que estamos (PC o Tablet PC), ya que solo rotará si estamos en modo Tablet.

cat /sys/devices/platform/thinkpad_acpi/hotkey_tablet_mode

Donde: 0 = laptop y 1 = tablet

Yo ya había hecho este script en bash, sin embargo, me pareció una buena práctica de scripting en python hacerlo por esta vía.

Por ejemplo, rotar la pantalla a la derecha sería en bash:

#!/bin/bash/usr/bin/xrandr -o right
xsetwacom set "Serial Wacom Tablet" Rotate cw
xsetwacom set "Serial Wacom Tablet eraser" Rotate cw

Nota: las rotaciones de la Tablet son: none, cw, ccw e inverted

Pueden descargar el guión de acá: http://depositfiles.com/files/wk2fdee34

Simplemente copian ambos archivos a /usr/bin:

cp * /usr/bin && chmod +x /usr/bin/rotate-screen.py

Y listo!, ya pueden rotar sus pantallas.

Auto-rotación

Para que la auto-rotación funcione, requieren que el módulo tp_smapi esté instalado, en Debian la forma más fácil es instalar el paquete DKMS (auto-construye el módulo):

aptitude install tp-smapi-dkms

Esto ejecutará lo siguiente:

Configurando tp-smapi-dkms (0.40-8) ...
Creating symlink /var/lib/dkms/tp-smapi/0.40/source ->                 /usr/src/tp-smapi-0.40
DKMS: add Completed.
Kernel preparation unnecessary for this kernel.  Skipping...
Building module:cleaning build area....make KERNELRELEASE=2.6.32-5-amd64 -C /lib/modules/2.6.32-5-amd64/build M=/var/lib/dkms/tp-smapi/0.40/build.....cleaning build area....
DKMS: build Completed.
thinkpad_ec.ko:Running module version sanity check. - Original module   - No original module exists within this kernel - Installation   - Installing to /lib/modules/2.6.32-5-amd64/updates/dkms/
tp_smapi.ko:Running module version sanity check. - Original module   - No original module exists within this kernel - Installation   - Installing to /lib/modules/2.6.32-5-amd64/updates/dkms/
hdaps.ko:Running module version sanity check. - Original module - Installation   - Installing to /lib/modules/2.6.32-5-amd64/updates/dkms/
depmod......
DKMS: install Completed.

Nota: Requieren tener instalados los linux-headers de su kernel actual

Si acaso desean instalarlo desde fuentes, entonces usan tp.smapi-sources:

aptitude install tp-smapi-source

Y usar module-assistant para instalarlo:

cd /usr/src/tp-smapi/

Actualizamos:

m-a update

Y auto-instalámos el módulo:

m-a a-i tp-smapi

luego ejecutamos el montaje de los módulos:

modprobe hdaps
modprobe thinkpad_ec
modprobe tp_smapi

Si acaso bajamos los fuentes de la página oficial (y no usamos los de Debian):

Descomprimen y compilan:

#tp_smapi

make
make install HDAPS=1
make load HDAPS=1 FORCE_IO=1

En cualquiera de los 3 casos, ejecutar modinfo tp-smapi devolverá información del módulo cargado:

modinfo tp-smapi
filename:       /lib/modules/2.6.32-5-amd64/updates/dkms/tp_smapi.ko
license:        GPL
version:        0.40
description:    ThinkPad SMAPI Support
author:         Shem Multinymous
srcversion:     F73BBCBD87C5944FFE67105
depends:        thinkpad_ec
vermagic:       2.6.32-5-amd64 SMP mod_unload modversions
parm:           debug:Debug level (0=off, 1=on) (int)

Cuando cargan, veremos esto en el dmesg:

[16848.875565] tp_smapi 0.40 loading...
[16848.876270] tp_smapi successfully loaded (smapi_port=0xb2).
[16855.810653] hdaps: LENOVO ThinkPad X61 detected, setting orientation 6
[16855.811656] hdaps: initial mode latch is 0x05
[16855.811824] hdaps: setting ec_rate=250, filter_order=2
[16855.812048] hdaps: device successfully initialized.
[16855.812811] input: ThinkPad HDAPS joystick emulation as /devices/virtual/input/input12
[16855.814213] input: ThinkPad HDAPS accelerometer data as /devices/virtual/input/input13
[16855.814571] hdaps: driver successfully loaded.

Ya con esto, la auto-rotación es posible!.

Auto-rotación, registrando un servicio

La auto-rotación es un script que queda “cargado en memoria” usando la capacidad de un script python de quedar registrado como un DBusGMainLoop, todo es gracias a esta función main:

def main():
  dbus_loop = DBusGMainLoop(set_as_default = True)
  session = dbus.SessionBus(mainloop = dbus_loop)
  loop = gobject.MainLoop()
  gobject.threads_init()
  gobject.timeout_add(1000,setRotation)
  loop.run()

main()

Que permite ejecutar la función “setRotation” cada 1000 milisegundos, dicha función se encuentra en el script “autorotate-screen.py” y determina la posición de la pantalla por las coordenadas de posición que devuelve el acelerómetro:

* Cuando hdaps está en modo “PC” en mi mesa:

cat /sys/devices/platform/hdaps/position

resultado : (0,-3)

* Cuando hdaps está en modo “tablet” con la posición invertida

cat /sys/devices/platform/hdaps/position

resultado : (-510,637)

Y como registro ese script?

Para que este script se inicie siempre que arranque la computadora (pero SOLO cuando YA haya arrancado las X) el script no debe ir en /etc/rc.local sino en:

/etc/gdm3/Init/Default

Al final de ese archivo agregaremos la línea:

/usr/bin/autorotate-screen.py

Y listo!, la próxima vez que el GDM inicie, podrán rotar la pantalla automáticamente (cuando esté en modo Tablet PC) con solamente girar la pantalla.

[python] usando gammu para enviar SMS

Continuando con el artículo “Enviando SMS usando bluetooth, un celular GSM y python”, voy a mostrar el uso de un toolkit bastante interesante para hacer más cosas con nuestro celular.

Gammu/gWammu es un proyecto que se ha fijado como meta manejar toda la información  de tu celular, cuando este está conectado a tu PC, sea via bluetooth o vía modem (USB, rs-232, etc).

Instalación:

En Debian y derivados es de lo más fácil:

aptitude install gammu gammu-smsd python-gammu

El primero es la aplicación GTK+ (fork de gnokii) para conectar celulares (no solo nokia como en gnokii) y administrar datos, contactos, etc.

La “phone database” es un compendio de todos los teléfonos donde ha sido probado gammu (si logras probar en algún otro, agrégalo como contribución.

Conectándose con Python

Para conectarnos a el teléfono, tenemos primero que configurarlo:

Para ello, creamos un archivo (por defecto, .gammurc  en la raíz del usuario):

[gammu]
port = 00:25:47:BC:8B:7F
connection = blueat

Pueden instalar wammu, y aprovechan el asistente de conexión a teléfonos, para que este descubra el tipo más válido de conexión para el suyo.

En mi caso, ha servido at115200 (USB) Para mi Motorola Rokr E8, para Nokia 5800 XpressMusic, aún no soporta comunicación para SMS.

Luego de conectados al teléfono (por USB, bluetooth), ejecutamos el script:

#!/usr/bin/env python
# Ejemplo de como enviar SMS usando gammu y python

import gammu
import sys

# crea un objeto stateMachine, para hablar con el telefono
sm = gammu.StateMachine()

# Leer la configuracion desde (~/.gammurc)
sm.ReadConfig()

# Iniciamos la conexión con el telefono
sm.Init()

# Preparamos el paquete de mensaje SMS
# SMSC es un numero que se puede obtener interrogando el telefono
message = {
 'Text': 'test SMS',
 'SMSC': {'Location': +584264568828},
 'Number': '+584264568828',
}

# Enviar el mensaje
sm.SendSMS(message)

Usando wammu

Para los que no deseen conectarse programáticamente, sino simplemente deseen conectarse a su teléfono, respaldar contactos o agenda, sms, enviar y recibir sms y demás, pueden usar la interfaz gráfica de gammu, llamada wammu; para instalarla:

aptitude install wammu

Con wammu (y dependiendo de marca, modelo, serie del sistema operativo, etc) podrán hacer “más o menos cosas”, cada vez están agregando más dispositivos a la “phone database”, en mi caso, el Nokia 5800 XpressMusic aún no dialoga muy bien con wammu (pero puedes aún enviar SMS usando py-bluez).

[PHP5] Obtener el SID de un dominio en modo nativo con ldap-toolkit

Fuente: https://code.google.com/p/php5toolkits/wiki/ObtenerSid

Obtener el SID de un Dominio Active Directory es parte *esencial* de una migración exitosa de un dominio MS a Samba; ya que este representará el SID del nuevo dominio.

Extraerlo usando ldap-toolkit es cosa bastante sencilla; como lo explico en el wiki del proyecto.

Configurar una conexión

Para usar una conexion a un MS Active Directory, configuramos:

#una conexion usando un Active Directory Service
$database['ads']['adapter']           = 'ads';
$database['ads']['host']              = '10.1.1.1';
$database['ads']['basedn']            = 'DC=test,DC=com,DC=ve';
$database['ads']['domain']            = 'test.com.ve';
$database['ads']['netbios_name']      = 'TEST';
$database['ads']['username']          = 'administrator';
$database['ads']['password']          = 'mipassword';
$database['ads']['port']              = 389;
$options = array('LDAP_OPT_PROTOCOL_VERSION' => 3, 'LDAP_OPT_REFERRALS'=>0, 'LDAP_OPT_SIZELIMIT'=>5000, 'LDAP_OPT_TIMELIMIT'=>300);
$database['ads']['options']           = $options;

#Importante el usuario y el netbios_name (nombre del dominio netbios) para el login

Snippet de Codigo

include "conf/base.inc.php";
include_once BASE_DIR . "conf/include_ldap.inc.php";

#obtenemos la configuracion AD
$ldap = ldap::load('ad');

#buscar una entrada basica, para extraer su SID
$entry = "(&(objectClass=user)(samaccounttype=". ADS_NORMAL_ACCOUNT .")(samaccountname=jesuslara))";
#conectamos al AD
$ldap->open();
$entry = $ldap->query($entry);
echo "SID usuario {$user}: " . $entry->bin_to_str_sid('objectSid');
$ldap->close();

Ese echo, mostrará un valor semejante a: ‘S-1-5-21-2102913520-367280043-1452191782-14800’ De esa salida: S-1-5-21-2102913520-367280043-145219178214800

Lo que está en negrillas es el SID del dominio, lo que es itálica, es el UidNumber del usuario; simplemente buscar la ubicación del último “-” y todo lo que hay desde ‘0’ hasta strpos(‘-‘) del último guión será el SID del dominio.

Fácil, rápido y permite una migración más transparente de dominios nativos MS AD a Samba.

[PHP5] Actualización de librerías

En vista de algunos proyectos de librerías que he hecho y que tengo por ahí pendientes; he creado un repositorio público en google code (que lo prefiero a GitHub para cosas como estas que no son proyectos, sino solo librerías); por donde la gente podrá encontrar algunas de las librerías que uso para algunos de mis proyectos.

En enlace: https://code.google.com/p/php5toolkits/

Entre las librerías que encontrarán ahí (por ahora, luego vendrán más) son:

excel_reader:

Una clase que permite leer archivos de excel (de los viejos, no de los OOXML, esa viene en camino) de una manera fácil y orientada a objetos; tiene un tiempo conmigo y hasta un artículo en este blog; pero había perdido acceso a mi viejo repositorio, así que le hice uno público.

la librería tiene licencia GPL v.2

Para hacer checkout, ejecutan:

svn checkout http://php5toolkits.googlecode.com/svn/tools/excel_reader/ excel_reader

Un ejemplo de uso:

$orden = new excel_reader('listado_sincedula.xls');
#de que columna sacamos los titulos de las columnas
$orden->setColumnName(1);
#definimos el encoding
$orden->setOutputEncoding('CP1251');

#ejecutamos la lectura del archivo excel:
$orden->read();

foreach($orden as $sheets) {
 echo "Hoja &lt <br />";
 $columnas = $orden->columns();
 $filas = $orden->numRows();
 for ($i = 2; $i <= $filas; $i++) {
 echo "Fila &lt <br />";
 $fila = $orden->rows($i);
 foreach($fila as $k=>$v) {
 print_r(nl2br("celda: {$k}= {$v}\n"));
 }
 }
 #forma alternativa
 foreach($orden->rows() as $fila) {
 foreach($fila as $k=>$v) {
 print_r(nl2br("celda: {$k}= {$v}\n"));
 }
 }
}

ldap-toolkit

Una mejora (usando una sintaxis orm-like) para operar con LDAP de una manera práctica; soporta las funciones más comunes que se realizan en LDAP; consultas, traer via DN, insertar, copiar, pegar, etc; permite obtener atributos privados y demás.

Para acceder al código:

svn checkout http://php5toolkits.googlecode.com/svn/ldap-toolkit

Y un ejemplo de uso uso:

#ejemplo de una nueva entrada:
$uid = $ldap->create('ou=usuarios,ou=Ejemplo,ou=unidades,dc=test,dc=com,dc=ve');
$uid->addObjectClass('inegOrgPerson'); #agrega recursivamente todos los objectclasses dependientes
$uid->addObjectClass('qmailUser');
$uid->baseAttribute('uid');
$uid->uid = 'prueba tres';
$uid->mail = 'prueba@test.com.ve';
$uid->insert();

Se aceptan sugerencias, revisión de código, ideas para ampliar las capacidades de las librerías, etc.

Enviando SMS usando bluetooth, un celular GSM y python

Estuve investigando una manera rápida, sencilla y con pocas líneas de código para poder enviar mensajes de texto usando un script, esto tiene utilidad para muchas aplicaciones, entre ellas el sistema nagios de monitoreo que estamos colocando en el trabajo.

La idea “adicional” de usar bluetooth surge de la posibilidad de evitar el uso de cables para transmitir los mensajes, para darle capacidades “bluetooth” a un equipo desktop convencional, simplemente agregamos un dongle bluetooth (que cuesta como 5 dólares en amazon) como este:

Y agregamos los paquetes necesarios:

aptitude install bluetooth bluez bluez-firmware bluez-utils python-bluez

Descubriendo dispositivos encendidos con python

El siguiente script (discovery.py) permite detectar qué equipos bluetooth están al alcance:

import bluetooth

lista = bluetooth.discover_devices(lookup_names = True)
print 'Lista de Dispositivos Bluetooth'
print 'Se encontraron %d' % len(lista)

for hwaddr, nombre in lista:
     print " %s - %s" % (hwaddr,nombre)

Nos mostrará algo como esto:

Se encontraron 1
 00:25:47:BC:8B:7F - Pheno

Determinación del Servicio:

El servicio Dial-Up Networking es el utilizado para enviar un mensaje SMS, el puerto dependerá del tipo de equipo y la cantidad de servicios asociados; si ejecutamos el comando:

sdptool browse 00:25:47:BC:8B:7F

Obtendremos algo como esto:

Service Name: Dial-Up Networking
Service RecHandle: 0x10019
Service Class ID List:
 "Dialup Networking" (0x1103)
Protocol Descriptor List:
 "L2CAP" (0x0100)
 "RFCOMM" (0x0003)
 Channel: 22
Language Base Attr List:
 code_ISO639: 0x454e
 encoding:    0x6a
 base_offset: 0x100
Profile Descriptor List:
 "Dialup Networking" (0x1103)
 Version: 0x0100

Reportando que el puerto para la conexión será el 22; pero esto también lo podemos hacer con Python, ampliando un poco el script discovery.py:

import bluetooth

lista = bluetooth.discover_devices(lookup_names = True)
print 'Lista de Dispositivos Bluetooth'
print 'Se encontraron %d' % len(lista)

for hwaddr, nombre in lista:
 print " %s - %s" % (hwaddr,nombre)
 services = bluetooth.find_service(address=hwaddr)
   if len(services) > 0:
     port = 0
     for svc in services:
       if  svc["name"] == "Dial-Up Networking":
       port = svc["port"]
       if port != 0:
         print "Encontrado Dial-Up Networking port = %d\n" % (port)

Al ejecutarlo reporta lo siguiente:

Se encontraron 1
00:25:47:BC:8B:7F – Pheno
Encontrado Dial-Up Networking port = 22

Sabiendo que nuestro dongle bluetooth funciona (y obviamente la mac-address de nuestro teléfono GSM con bluetooth), procedemos al siguiente paso, que es enviarnos mensajes de texto.

Enviando SMS con python

Con el siguiente script, podemos enviar un SMS usando GSM:

import bluetooth

#bluetooth address
addr = '00:25:47:BC:8B:7F'

#puerto
port = 22

#telefono
mobile = '584xxxxxxxxx' #de la forma 58 4[operadora][numero]
#mensaje a enviar
data = 'Test SMS'

# crear un socket bluetooth rfcomm
socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)

# la funcion connect pide los parametros direccion y puerto (address, port)
socket.connect((addr, port)) #abrir un socket al bt
#inicializamos el dialogo AT con el modem
socket.send('ATZ\r')
print socket.recv(1024)
#valor "1" indica que el SMS es solo texto
socket.send('AT+CMGF=1\r')
print socket.recv(1024)
#Telefono al que enviaremos el SMS
socket.send('AT+CMGS="+\"mobile\""\r')
print socket.recv(1024)
socket.send(data+chr(26)) #data + CTRL+Z
print socket.recv(1024)

socket.close()

La salida del script será algo como esto:

ATZ
OK

AT+CMGF=1
OK

AT+CMGS="+584xxxxxxxxx"
>
Test SMS
OK

Notas adicionales

Pueden agregar la configuración del dispositivo en /etc/bluetooth/rfcomm.conf para que haga enlace automáticamente y no esté solicitando bind+contraseña (y sea una conexión en confianza entre el teléfono y el PC):

rfcomm0 {
# Automatically bind the device at startup
bind yes;

# Bluetooth address of the device
device 11:22:33:44:55:66;

# RFCOMM channel for the connection
channel 1;

# Description of the connection
comment “Mobile Phone Pheno”;
}

Agregan la entrada y reinician el demonio bluetooth

/etc/init.d/bluetooth restart

TODO

* Aprovechar los posixGroups del LDAP y usar py-ldap para iterar sobre todos los miembros de un grupo (ejemplo: Soporte o Redes), extraer su atributo “mobile” y pasarlo al script como número a enviar (pueden ser varios destinatarios).

* Integrar discovery.py y send_sms.py en un único script, así el script descubre la dirección, el puerto, abre el socket, envía el SMS y se desconecta (con import sys podemos hacer sys.exit(0) para salir del script con confianza).

* Agregar excepciones (como el bluetooth.btcommon.BluetoothError 112; Host is Down) para gestión de errores del script.

[PHP5] – Barco Project: Un proyecto sobre barcodes y QRCode

En el día de hoy (30. sep. 2009) nace Barco Project como una iniciativa de integrar bajo una misma API todo el proceso de generar y analizar (escanear) variados tipos de Codigos de Barra y Códigos 2D; esto integra la posibilidad de con una misma API, generar:

  • Code39 (Code 3 to 9)
  • EAN 128
  • UPC
  • UPCS (US Postal Code)
  • Codabar
  • UPCa
  • Code128
  • QRCode
  • ISBN

Además incluirá la posibilidad de “analizar” un PNG generado para “reconocer” el contenido de algunos códigos de barra arriba mencionados.

El proyecto se encuentra en fase de “alpha release” y actualmente solo ha sido “portado” el código para generar QRCode.

Qué es QRCode?:

Según la Wikipedia un QRCode es: “Un QRCode (Quick Response Barcode) es un sistema para almacenar información en una matriz de puntos o un código de barras bidimensional creado por la compañía japonesa Denso-Wave en 1994; se caracterizan por los tres cuadrados que se encuentran en las esquinas y que permiten detectar la posición del código al lector. La sigla “QR” se derivó de la frase inglesa “Quick Response” pues el creador aspiraba a que el código permitiera que su contenido se leyera a alta velocidad. Los códigos QR son muy comunes en Japón y de hecho son el código bidimensional más popular en ese país.” (fuente: wikipedia).

Acá un ejemplo de código QR:

Y qué tiene de intersante QR?

QRcode es de muchisima popularidad en japón, esto es debido a que mientras el resto de los códigos de barra requieren la detección de “espacios y llenos” utilizando un láser; QRCode usa un patrón de cuadrados (dot-matrix) que representan los datos (en 2 dimensiones, ancho y alto) y los “cuadrados marca” sirven para que cualquier lectór óptico reconozca donde empieza y termina “el código” (entiendase por cualquier lector a cámaras digitales y otros dispositivos ópticos).

Además QR es un proyecto de “código abierto”; desarrollado por Densa-wave (una filial de Toyota) para el inventario de repuestos de vehículos; sin embargo, tras la liberación de patentes (royalty-free) hecha en 1999 y su correspondiente estandarización como norma ISO-IEC (ISO/IEC18004) en 2000 ha permitido a mucha gente desarrollar aplicaciones para QR.

Y como ha ganado tanta popularidad?

QR viene en patrones fijos de 1 a 40 cuadros, pudiendo albergar hasta 7000 carácteres, como es posible guardar tal cantidad de información y como es posible “escanearlo” con cualquier cámara óptica (además es completamente libre hacerlo), pues 2+2!, a mucha gente en Japón se les ocurrió “incorporar” lectores de QR en sus respectivos celulares, esto fue ganando popularidad hasta el punto que en japón la gente coloca su QR informativo en el reverso de las tarjetas de presentación y hasta la información de ubicación en las revistas viene en códigos QR (ahorras espacio de publicación).

Nokia tiene su aplicación “oficial” para QRCode y Datamatrix para todos sus dispositivos serie N (aunque funciona en mi Nokia 5800 XpressMusic) acá:

http://mobilecodes.nokia.com/

Acá hay alguien que habla de una aplicación para Blackberry para analizar QRcodes:

http://www.enriquedominguez.com/leyendo-qr-codes-desde-la-blackberry/

iMatrix es la aplicación “oficial” del Iphone para leer QRCodes:

http://www.qrcode.es/?p=244&language=es

Por lo que un QRCode generado ya casi cualquier persona (que tenga cámara digital en el celular + el software) lo puede leer.

Montandose en el barco de Barco Project

Barco Project surge como una iniciativa personal de integrar dentro de una única librería todo lo necesario para generar diversos tipos de barcodes; realmente todo surge como la posibilidad de cumplir con la ordenanza de la Providencia 939 del SENIAT (Organismo tributario Venezolano) que indica que para las facturas electrónicas impresas en formas libres; se sugiere agregar el código de barra para identificar la factura y algún medio electrónico de identificación automatizado del cliente (nombre, RIF: Registro de Información Fiscal).

Para ambas cosas, que mejor que combinar Code39 (factura), UPC (Inventario) y QRCode (identificación)?.

En mi investigación, determiné que el único script “viable” para QRCode en PHP que existía estaba en Japones y solamente funcionaba en CGI (es una pagina web que te pide todos los parámetros como URL encode data); esto dista mucho de la practicidad de contar con un objeto parametrizable que permita a distintas aplicaciones generar códigos QR.

Barco::QR

Con Barco::QR un programador de aplicaciones puede generar códigos QR con la simplicidad de un código PHP; veamos un ejemplo:

Primero, inicializamos la librería barco:

require 'barco/barco.inc.php';
#Inicializamos Barco:
barco::init();

Esta línea, me permite solicitar un generador de códigos QR (Quick Response):

$qr = barco::generate('qr');

La gran mayoría de los parámetros vienen por defecto, el único valor obligatorio es los datos que debemos agregar:

#defino la data y el modo de correccion ECC;
$qr->data('Esta es una prueba de Encoding usando QRcode')->ecc('M')->size('10');

ECC es el módo de corrección de errores, QR cuenta con 4 modos (‘M’, Q, L y H) (esto es opcional: por defecto siempre es M).

El Tamaño (size) define las dimensiones máximas del QR, desde 1 (max: 35 caracteres) hasta 40 (max: 7000).

Nota: intentar crear un QR más pequeño que la cantidad de datos genera error, aunque espero “mejorar” ese comportamiento al tratar de determinar el tamaño óptimo para la longitud de los datos requeridos.

Ahora, simplemente generamos el código QR:

if($qr->generate()) {
 $qr->image('png');
}

QR puede emitir el código como un RAW de bytes de imagen (útil para almacenar en una DB) o en forma de una imágen para salida por navegador, también pueden almacenarlo con un nombre a un archivo, escogiendo claro está, si lo desean jpeg o png.

El código generado por este ejemplo es:

Esta es una prueba de Encoding usando QRcode

Esta es una prueba de Encoding usando QRcode (10x10)

Como indiqué el proyecto se encuentra en fase “pre-alpha” y ya genera códigos QR (a diferentes dimensiones), próximamente incorporaré Code39, UPC y DataMatrix como principales algorítmos de códigos de barra.

Tambien incorporaré un lector-analizador (usando imagemagick) de códigos de barra para que puedan “probar” los códigos generados en ausencia de un scanner.

Los Códigos QR generados por el proyecto han sido probados con mobilecodes de Nokia y con QuickMark para HTC (espero más gente los pruebe).

Esperen más noticias de este proyecto!.

Nota: próximamente incorporaré un servicio gratuito de generación de QRCodes para personas que solo deseen tener un QR y no tengan tiempo (o no sepan) programar; esperalo!.

PHP5 – TDOM: Creando archivos XML usando DOM y PHP

En este post presento TDOM (Tomato:Document Object Model), una librería que permite simplificar el proceso de leer, analizar, modificar o crear archivos XML-based de distintos tipos usando una API simplificada y utilitaria.

Aunque esta librería tiene ya bastante tiempo conmigo (a menos más de un año desde que la hice); un “re-factoring” para hacerla más compatible con PHP > 5.3 y la posibilidad de tener una interfaz basada en “plugins” que permiten extender sus capacidades agregando nuevos tipos de documentos XML y además de crearle un proyecto libre en Google Code; es lo que me hace escribir el articulo.

El proyecto nace bajo licencia GPL v.3 y está en fase alpha, versión 0.1.2; permite crear documentos y archivos en:

  • XML
  • DocBook
  • SVG
  • (x)HTML estricto
  • Interfaces XUL remote

Espera incorporar próximamente (ya estoy programando en ello):

  • XMPP (Paquetes del protocolo XMPP que usan Jabber/Gtalk)
  • RSS (Sindicación de manera fácil)
  • RDF (Resource Description es base de muchos tipos de archivos, como FOAF: relaciones amigo-amigo descritas semánticamente usando RDF)
  • ATOM
  • ODF

TDOM forma parte de la capa de creación y vistas de Tomates Framework, que se encuentra en un proceso final de adaptación y re-invención; lo importante de TDOM es que permite crear interfaces XUL, combinado con Jquery+REST sería el primer framework completamente operativo sobre PHP (el otro es Cyclone en perl) que permitiría crear aplicaciones XUL:Remote (que correrían como una aplicación desktop usando XULRunner) en implementar completamente Interfaces XUL de Mozilla.

Creando una Interface XUL

Está claro que el código dentro de poco será más simplificado cuando todos los widgets UI (toolbars, menu, tabbers, botones) estén incorporados en la clase; pero este es un ejemplo escribiendo solo PHP:

$xml = $dom->type('xul');
#un tabbox
$tabbox = $xml->create('tabbox');
$tabbox->orient('vertical')->flex('1');
$tabs = $tabbox->create('tabs');
$tabpanel = $tabbox->create('tabpanels');
$tabpanel->flex('1');
#adjunto las pestañas:
$tabs->create('tab')->label('Google');
$tabs->create('tab')->label('PHP.net');
$tabs->create('tab')->label('DEVEL');
#y el contenido de las pestañas
$tabpanel->create('browser')->src('http://www.google.co.ve/');
$tabpanel->create('browser')->src('http://www.php.net/');
$tabpanel->create('browser')->src('http://www.devel.com.ve/');
$xml->title('Prueba de XUL');
$dom->render(); 

Output:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="xulWindow" persist="screenX screenY width height sizemode" xmlns:html="http://www.w3.org/1999/xhtml" title="Prueba de XUL">
  <tabbox orient="vertical" flex="1">
    <tabs>
      <tab label="Google"/>
      <tab label="PHP.net"/>
      <tab label="DEVEL"/>
    </tabs>
    <tabpanels flex="1">
      <browser src="http://www.google.co.ve/"/>
      <browser src="http://www.php.net/"/>
      <browser src="http://www.devel.com.ve/"/>
    </tabpanels>
  </tabbox>
</window>

Proximamente subiré una demo de como se ve esta interfaz XUL.

Creando un archivo (x)HTML estricto

Un ejemplo de como el código puede ser simplificado, es tener todo un cuerpo XHMTL 1.0 estricto teniendo solo 3 líneas de código; esto gracias a que todas las etiquetas son necesarias para la descripción de un archivo según la norma de la W3c; estas 3 líneas:

$html = $dom->type('html'); 
$html->setTitle('.: Titulo de la Pagina Web :.');
$dom->render();

Generan todo este bloque de código que es sintácticamente válido con XHTML 1.0 estricto; lo interesante es que podemos cargarle contenido al BODY con TDOM y este será normalizado para ser XHTML 1.0 estricto; básta de aplicaciones inválidas.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://localhost/proyectos/tomates/tomates/include/tdom/tomates/tdom/include/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.: Titulo de la Pagina Web :.</title>
    <base href="http://localhost/proyectos/tomates/" />
<!--Aqui comienzan las etiquetas meta-->
    <meta name="author" content="" />
    <meta name="generator" content="Tomates Framework" />
    <meta name="description" content="" />
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <meta name="robots" content="all" />
    <meta http-equiv="Content-Language" content="en_US" />
    <meta name="keywords" content="" />
    <meta name="Revised" content=": Sep 18 2009" />
<!--Aqui terminan las etiquetas meta-->
    <link rel="shortcut icon" href="http://localhost/proyectos/tomates/application/assets/images/favicon.ico" type="image/x-icon" />
    <link rel="start" href="http://localhost/proyectos/tomates/" />
    <link rel="index" href="http://localhost/proyectos/tomates/" />
  </head>
  <body></body>
</html>

Creating an (x)HTML strict

Code:

                $html = $dom->type('html');
                $html->createHead();
                $html->setTitle('.: Titulo de la Pagina Web :.');
                $dom->render();

Output:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://localhost/proyectos/tomates/tomates/include/tdom/tomates/tdom/include/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.: Titulo de la Pagina Web :.</title>
    <base href="http://localhost/proyectos/tomates/" />
<!--Aqui comienzan las etiquetas meta-->
    <meta name="author" content="" />
    <meta name="generator" content="Tomates Framework" />
    <meta name="description" content="" />
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <meta name="robots" content="all" />
    <meta http-equiv="Content-Language" content="en_US" />
    <meta name="keywords" content="" />
    <meta name="Revised" content=": Sep 18 2009" />
<!--Aqui terminan las etiquetas meta-->
    <link rel="shortcut icon" href="http://localhost/proyectos/tomates/application/assets/images/favicon.ico" type="image/x-icon" />
    <link rel="start" href="http://localhost/proyectos/tomates/" />
    <link rel="index" href="http://localhost/proyectos/tomates/" />
  </head>
  <body></body>
</html>

Map / Reduce en PHP comparado con Scheme

PHP6 será compatible con funciones lambda y agregará por ende algunas caracteristicas de lenguajes funcionales; interesante es entonces encontrar entradas como esta, que nos explica de una manera simple y sencilla la implementación de una “high-order function” en PHP6 comparandola luego con Scheme (un lenguaje funcional).

Veamos el ejemplo comparando la función MAP en PHP6 y en Scheme:

function map($fn, $list) { 
  return empty($list) ?
  array() :
  construct($fn(first($list)),
  map($fn, rest($list)));
}

Y ahora vemos la misma función equivalente en scheme:

(define (map fn lis)
 (if (null? lis)
 '()
 (cons (fn (car lis))
 (map fn (cdr lis)))))

Las funciones anónimas solventan muchos problemas que antes se solventaban con loops recursivos bastante peligrosos; que vamos a estar claros no aporta la potencia de Lisp o Scheme, pero ya es un avance en el lenguaje.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 3.601 seguidores

A %d blogueros les gusta esto: