Introducción a Comet y aplicaciones RIA
28/07/2009
En la introducción de este artículo, quiero citar un poco de historia contemporánea acerca de tecnologías web. Hasta no hace mucho mas de 5 años, las aplicaciones web based, tenían un gran dilema: cada vez que se necesitaba mostrar los datos que han resultado de peticiones al server, se debía recargar la pagina en su totalidad. Lo que producía que las aplicaciones fueran bastante limitadas en cuanto a interactividad, velocidad, usabilidad y tiempos de respuesta. Cuando aparece AJAX (Asynchronous JavaScript And XML) allá por 2005, acerca a la gran mayoría de la comunidad de desarrollo el concepto de comunicacion asincrona con el servidor en segundo plano y abre todo un mundo de posibilidades, comenzando y produciendo un gran cambio que ha revolucionado la web. Aparecen aplicaciones web based que empiezan a asemejarse bastante a aplicaciones de escritorio. Permitiendo construir aplicaciones con UI's más potentes, amigables e intuitivas.
Viendo toda esta revolución pasar por nuestras narices, el desarrollo de aplicaciones RIA (Rich Internet Applications) nos exige ir un poco mas allá. Por ejemplo, supongamos que debemos desarrollar una aplicación de mail, con su correspondiente bandeja de entrada. Esta aplicación necesitaría que cada cliente (browser) este actualizado al instante, en cuanto a las novedades que ocurren en el server que tienen que ver con él. Es decir, cada usuario de nuestra aplicación de mail tiene que enterarse de que ha llegado un nuevo correo que lo tiene como destinatario.
De este ejemplo se desprende la necesidad de tener un canal bidireccional de comunicación entre servidor y cliente. Digamos, que en ciertas oportunidades, cuando el servidor tiene novedades en relación a un cliente, sea este quien envie los datos sin necesidad que el cliente este constantemente realizando peticiones acerca de novedades. Para simular o lograr esta comunicación necesaria entre cliente y server, existen varias técnicas que comentaremos a continuación.
PollingEn la técnica de Polling, un browser abre una conexion con el server realizando peticiones repetidamente cada x segundos para estar al tanto de las novedades que a él respectan. En caso de no tenerlas el servidor retorna una respuesta vacía. Es decir, en nuestra aplicacion Mail de ejemplo, tendríamos a los clientes realizando peticiones cada 10 segundos para saber si ha llegado un nuevo mail que lo tenga como destinatario.
Long PollingLong Polling es una variación de la técnica tradicional de polling. Con esta técnica, el cliente solicita información al servidor de forma similar al polling normal. Sin embargo, si el servidor no tiene información disponible para el cliente, en lugar de enviar una respuesta vacía, el servidor retiene la solicitud y espera a que cierta información esté disponible. Una vez que la información está disponible (o después de un cierto tiempo), una respuesta completa, se envía al cliente. El cliente, normalmente, en forma inmediata vuelve a solicitar la información al servidor, de manera que el servidor casi siempre tiene una solicitud disponible en espera para así poder utilizarla para proporcionar datos en respuesta a un evento.
CometComet también es conocido como server push, HTTP push, HTTP streaming, Pushlets, Reverse Ajax, y otros. El objetivo de esta técnica es permitir a los servidores web enviar datos al cliente sin ninguna necesidad de que éstos se lo soliciten. O sea, que el cliente no pida al servidor, sino que el servidor envíe datos al cliente. Yendo a nuestra aplicación Mail de ejemplo, cada cliente se subcribiría a la espera de novedades. Luego cuando el servidor detecte ha llegado un nuevo mail que tiene como destinatario al cliente, el servidor tomará la posta y será quien avise provea al cliente la información sobre las novedades.
Hemos hecho un resumen sobre la revolución que ha provocado AJAX en la web. Dejamos planteado el problema que nos encontramos al desarrollar aplicaciones RIA. Y hemos hablado de las técnicas Polling, Long Polling y Comet.
Solo resta comentar hacia donde va el futuro de todo esto, quizás la respuesta la podremos encontrar en la especificación de HTML5 donde se prevee, a través de la característica de Web Socket's, permitir establecer un canal de comunicación bi-direccional entre servidor y cliente.
TSearch2 para PostgreSQL
24/07/2009
O cómo buscar rápidamente en campos de texto en PostgreSQL a partir de la versión 8.2. Para ello implementa un tipo especial de índice que puede ser usado para la indexación de texto completo. Antes de entrar en detalles, mencionar que para que funcione el script debes tener instalado PostgreSQL con el módulo Tsearch2 y el diccionario en el correspondiente idioma, en nuestro caso 'spanish'. Es importante tener bien instalado y configurado el diccionario para que genere correctamente los lexemas.
El siguiente ejemplo muestra un ejemplo de búsqueda en una tabla que almacena titulos de películas y la sinopsis. En lo que nos ayuda Tsearch2, además de la velocidad de respuesta de la consulta, es capaz de otorgar pesos a las palabras que buscamos. veremos que se trata de una poderosa herramienta para búsqueda de texto
Estructura de la tabla
CREATE TABLE film
(
id_film int4,
titulo varchar(100),
sinopsis text
);
Ahora, a llenarla, por ejemplo, con un millón de registros. ;)
INSERT INTO film
VALUES ('1', 'Grease', 'En 1958, durante sus vacaciones de verano, Danny Zuko (John Travolta) y Sandy Olsson (Olivia Newton-John) se reúnen en una playa. Escenas de sus vacaciones se muestran, pero el verano termina, y Sandy y Danny decir su último adiós...');
INSERT INTO film
VALUES ('2', 'No es lugar para viejos', '...');
INSERT INTO film
VALUES ('3', 'Dos hombres y un destino', '...');
[...]
El siguiente paso es crear el campo especial del tipo
tsvector con el siguiente comando.
ALTER TABLE film ADD COLUMN idx tsvector;
Marcamos el peso de cada campo con las letras A, B, C, D. Esto nos sirve para indicar en que campo o columna de nuestra tabla tienen más relevancia los térrminos posteriores de búsqueda. En nuestro ejemplo estamos indicando que el texto búscado es más relevante si aparece en el título que en la sinopsis.
La función
to_tsvector genera el contenido del campo idx similar a los siguiente:
"Dos hombres y un destino";"'dos':1 'hombr':2 'destin':3"
UPDATE film SET idx = ( setweight ( to_tsvector (titulo), 'A') ||
setweight ( to_tsvector (sinopsis), 'B' ));
Generamos el índice con la función
gin (Indice Generalizado Invertido), que según la documentación de PostgreSQL proporciona una forma más escalable y programable de indexar datos semi-estructurados y texto.
CREATE INDEX idx_films ON film USING gin(idx);
Pues ya sólo nos queda probar una consulta. Hemos utilizado
ts_headline que sirve para marcar en negrita las palabras encontradas en la cadena de texto devuelta.
Para establecer el criterio de ordenación utilizamos
ts_rank_cd que asigna un valor al peso asociado a cada campo anteriormente con
setweight. Los valores pueden oscilar entre 0 y 1 y en éste caso hemos otorgado un 90% al título (marcado como A) y un 10% a la descripción (marcado como B).
SELECT titulo, descripcion, therank,
ts_headline(titulo || ' ' || descripcion, q) as summary
FROM (SELECT titulo, descripcion,
ts_rank_cd('(0.9,0.10)', idx, q) as therank, q
FROM film,
to_tsquery('(chocolate | secretaria | coche) & (arnold )') as q
WHERE idx @@ q
ORDER BY therank DESC
LIMIT 3) As results;
Subir un fichero de forma segura
19/02/2009
Lo que parece algo trivial como permitir a los usuarios de nuestra aplicación en php subir un fichero a través de un formulario web, puede ocasionarnos graves problemas de seguridad en nuestro servidor.
Debemos, por tanto, establecer controles de seguridad en nuestro script de upload en php para no permitir que nos lleguen ficheros como el siguiente.
<?php
system($_GET['comando']);
?>
Éste es un script sencillo con el que podemos ejecutar comandos de shell a través de la url http://servidor/shell.php?comando=comando_shell_unix. Pero existen muchos más avanzados y peligrosos.
En el siguiente script de upload en php, tenemos en cuenta las siguientes verificaciones para evitarlo.
- Verificar el "content-type"
- Verificar la extensión del fichero
- Acceso indirecto a los ficheros subidos
<?php
$imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
echo "Sólo se aceptan ficheros GIF y JPEG \n"; # Es un ejemplo, podemos montar un array con los tipos de fichero permitidos
exit;
}
$blacklist = array(".php", ".phtml", ".php3", ".php4", ".php5");
foreach ($blacklist as $item) {
if(preg_match("/$item\$/i", $_FILES['userfile']['name'])) {
echo "No está permitido subir ficheros PHP\n";
exit;
}
}
$uploaddir = '/var/spool/uploads/'; # Fuera del web root
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "Fichero válido y fue correctamente subido.\n";
} else {
echo "Error al subir el fichero.\n";
}
?>
Ahora no es posible acceder a /uploads/ para ver los ficheros subidos, por lo tanto es necesario el siguiente script adicional para obtener los ficheros.
<?php
$uploaddir = '/var/spool/uploads/';
$name = $_GET['name'];
readfile($uploaddir.$name);
?>
Todavía podemos darle una vuelta más de seguridad generando un nombre aleatorio a cada archivo subido y almacenar en un base de datos el valor de referencia.
<?php
require_once 'DB.php'; # Métodos de acceso a la base de datos
$uploaddir = '/var/spool/uploads/'; # Fuera del web root
$uploadfile = tempnam($uploaddir, "upload_");
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
# Guardar información sobre el fichero en la BD
$db = DB::connect("datos_conexion");
$res = $db->query("INSERT INTO uploads SET name=?, original_name=?, mime_type=?", array(basename($uploadfile, basename($_FILES['userfile']['name']), $_FILES['userfile']['type']));
}
?>
En éste caso, el script para acceder al fichero subido podría ser el siguiente.
<?php
require_once 'DB.php'; # Métodos de acceso a la base de datos
$uploaddir = '/var/spool/uploads/';
$id = $_GET['id'];
$db = DB::connect("datos_conexion");
$file = $db->getRow('SELECT name, mime_type FROM uploads WHERE id=?', array($id));
if(is_null($file) || count($file)==0) {
die("Fichero no encontradro");
}
header("Content-Type: " . $file['mime_type']);
readfile($uploaddir.$file['name']);
?>
Auto carga de objetos utilizando la función __autoload de PHP5
25/07/2008
Cuando desarrollamos con PHP sobre el paradigma Orientado Objetos, cada clase es definida en un script php. Por lo que para poder utilizar dichas clases, se deberán hacer los includes necesarios al principio de cada script. Dependiendo de la complejidad de la aplicación, puede terminar siendo, una larga y tediosa lista de includes.
En PHP5 esto se puede evitar. Sólo hay que definir una función llamada __autoload, la cual es llamada automáticamente en el caso de que se intente usar una clase que no haya sido incluida en el script. Es la última chance de cargar la clase antes de que PHP lance un error.
Otra de las ventajas de utilizar __autoload, es que evitamos se incluyan definiciones de clases que no utilizamos. Esto mejorará el rendimiento de nuestras aplicaciones ya que solo se cargan las clases que necesitamos. Por otro lado, se debe tener en cuenta de que la función __autoload será llamada recurrentemente, por lo que tendrá que ser definida lo más liviana posible.
<?php
function __autoload($className) {
if (file_exists("./lib/$className.class.php") ){
include_once("./lib/$className.class.php");
}
}
$instancia = new unaClase();
?>
En el caso de que estemos desarrollando una aplicación web o escribiendo código que puede ser utilizado por terceros, no es buena idea usar la función __autoload, dado que podría generar eventualmente un conflicto con otra función __autoload definida por terceros. Esto sucede porque lógicamente no puede haber dos funciones con el mismo nombre registradas. Lo que conviene es o bien declararla dentro de una clase o llamarla con otro nombre y luego registrarla en ambos casos mediante la función
spl_autoload_register<?php
spl_autoload_register(array('OP', '__autoload'));
class OP{
private static
$dir_class= array('util',
'controller',
'model',
'session',
'i18n');
public static function __autoload($class) {
foreach(self::$dir_class as $dir){
if(file_exists("./lib/$dir/$class.class.php")){
include_once("./lib/$dir/$class.class.php");
}
}
}
}
function miAutoload($class) {
include_once("./$class.php");
return true;
}
spl_autoload_register('miAutoload');
//Registramos otra vez la misma función
spl_autoload_register('miAutoload');
//Mostramos las funciones registradas
var_dump(spl_autoload_functions());
?>
Podremos ver que la función miAutoload esta registrada solo una vez.
array(2) { [0]=> array(2) { [0]=> string(2) "OP"
[1]=> string(8) "__autoload" }
[1]=> string(10) "miAutoload" }
Integrar en tu web marcadores de del.icio.us con cURL y PHP5
25/07/2008
Antes que nada explicaré brevemente que es del.icio.us. Es un servicio de gestión de marcadores al estilo red social que permite agregar los marcadores que usualmente se guardan en los navegadores, categorizarlos con el sistema de Tags, compartirlos con otros usuarios, acceder a ellos desde cualquier ordenador conectado a internet, etc. Y además dispone una potente pero sencilla API que utlizaremos para el propósito de éste artículo, integrar los marcadores almacenados en del.icio.us en nuestra web utilizando PHP y cURL. De ésta forma conseguiremos mantener la lista de enlaces de nuestra web al mismo tiempo que mantenemos nuestra lista de enlaces particular.
¿Qué es cURL? PHP soporta libcurl, librería que permite conectar y establecer comunicación con diferentes tipos de servidores con diferentes protocolos como http, https, ftp, gopher, telnet, dict, file, ldap. Soporta certificados https, autenticación de usuarios, etc. En php dispone de una colección de funciones con el prefijo curl_* , así que vamos a utilizar ésta fantástica librería para comunicarnos con del.icio.us.
La API de del.icio.us es sencilla, consiste en peticiones HTTPS con autenticación HTTP-Auth. Utilizaremos el método POSTS (así denomina del.icio.us a los marcadores, así los denominaré a partir de ahora) que permite hacer diferentes acciones sobre éstos. La que nos interesa es la que obtiene el listado de posts, además con la opción de filtrarlos por un tag.
Está disponible en
http://del.icio.us/help/api/.
Ésta sería la petición:
https://api.del.icio.us/v1/posts/get?tag=public
Nos devolvería en formato XML los posts etiquetados con public, que serán los que queremos que se muestren en la web. Pues vamos a ver como realizar está petición con cURL.
$peticion = https://api.del.icio.us/v1/posts/get?tag=public;
if (function_exists('curl_init')) {
$o_curl = curl_init($peticion);
curl_setopt_array($o_curl, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'http://www.miweb.com',
CURLOPT_CONNECTTIMEOUT => 5, //segundos
CURLOPT_TIMEOUT =>10 //segundos,
CURLOPT_USERPWD => 'username:password'
));
if ($result = curl_exec($o_curl)) {
switch (curl_getinfo($o_curl, CURLINFO_HTTP_CODE)) {
case 200:
$posts = $result;
break;
case 503:
//lanzar error. Bloqueo de la petición temporalmente.
default:
//lanzar error de conexión a del.icio.us
}
curl_close($o_curl);
}
La función
curl_setopt_array() setea las opciones de la petición.
- CURLOPT_RETURNTRANSFER. Lo seteamos a TRUE para devolver el resultado como una cadena de texto que contiene el valor devuelto por la función curl_exec(), en vez de mostrar la salida directamente en la ventana del navegador.
- CURLOPT_USERAGENT.El contenido de la cabecera "User-Agent: " enviada en las peticiones HTTP. Es un requisito de la API de del.icio.us. Por ejemplo podemos poner la url de nuestra web.
- CURLOPT_CONNECTTIMEOUT. El número de segundos que se pueden esperar como máximo intentando establecer la conexión.
- CURLOPT_TIMEOUT. El número máximo de segundos que cURL espera a que se ejecuten las funciones.
- CURLOPT_USERPWD. La API requiere autenticación. Aquí seteamos el nombre de usuario y contraseña con el formato "username:password".
Con
curl_getinfo obtenemos el codigo http para comprobar si ha habido algún error. Cabe destacar el error 503, devuelto cuando del.icio.us bloquea la petición por un tiempo si se realizan muchas en un corto intervalo de tiempo. Por lo que sería conveniente añadir un sistema de
cache en un archivo de texto para limitar el número de peticiones. El resultado de la petición lo devuelve en formato XML, por lo que tendríamos que parsearlo para mostrarlo en nuestra web. Pero ésto ya es otro tema.
Obtención de Google Pagerank desde PHP5 usando sockets
18/07/2008
El pagerank de una página es un excelente indicador de la calidad de la misma, ya que por lo general Google asigna pageranks altos a las páginas con mejores contenidos. Utilizando PHP5 podemos obtener el pagerank de una página determinada usando el siguiente fragmento de código:
<?php
$googlehost='toolbarqueries.google.com';
$googleua='Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5';
function StrToNum($Str, $Check, $Magic) {
$Int32Unit = 4294967296;
$length = strlen($Str);
for ($i = 0; $i < $length; $i++) {
$Check *= $Magic;
if ($Check >= $Int32Unit) {
$Check = ($Check - $Int32Unit * (int) ($Check / $Int32Unit));
$Check = ($Check < -2147483648) ? ($Check + $Int32Unit) : $Check;
}
$Check += ord($Str{$i});
}
return $Check;
}
function HashURL($String) {
$Check1 = StrToNum($String, 0x1505, 0x21);
$Check2 = StrToNum($String, 0, 0x1003F);
$Check1 >>= 2;
$Check1 = (($Check1 >> 4) & 0x3FFFFC0 ) | ($Check1 & 0x3F);
$Check1 = (($Check1 >> 4) & 0x3FFC00 ) | ($Check1 & 0x3FF);
$Check1 = (($Check1 >> 4) & 0x3C000 ) | ($Check1 & 0x3FFF);
$T1 = (((($Check1 & 0x3C0) << 4) | ($Check1 & 0x3C)) <<2 ) | ($Check2 & 0xF0F );
$T2 = (((($Check1 & 0xFFFFC000) << 4) | ($Check1 & 0x3C00)) << 0xA) | ($Check2 & 0xF0F0000 );
return ($T1 | $T2);
}
function CheckHash($Hashnum) {
$CheckByte = 0;
$Flag = 0;
$HashStr = sprintf('%u', $Hashnum) ;
$length = strlen($HashStr);
for ($i = $length - 1; $i >= 0; $i --) {
$Re = $HashStr{$i};
if (1 === ($Flag % 2)) {
$Re += $Re;
$Re = (int)($Re / 10) + ($Re % 10);
}
$CheckByte += $Re;
$Flag ++;
}
$CheckByte %= 10;
if (0 !== $CheckByte) {
$CheckByte = 10 - $CheckByte;
if (1 === ($Flag % 2) ) {
if (1 === ($CheckByte % 2)) {
$CheckByte += 9;
}
$CheckByte >>= 1;
}
}
return '7'.$CheckByte.$HashStr;
}
function getch($url) { return CheckHash(HashURL($url)); }
function getpr($url) {
global $googlehost,$googleua;
$ch = getch($url);
$fp = fsockopen($googlehost, 80, $errno, $errstr, 30);
if ($fp) {
$out = "GET /search?client=navclient-auto&ch=$ch&features=Rank&q=info:$url HTTP/1.1\r\n";
$out .= "User-Agent: $googleua\r\n";
$out .= "Host: $googlehost\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
$data = fgets($fp, 128);
//echo $data;
$pos = strpos($data, "Rank_");
if($pos === false){} else{
$pr=substr($data, $pos + 9);
$pr=trim($pr);
$pr=str_replace("\n",'',$pr);
return $pr;
}
}
fclose($fp);
}
}
function pagerank($url) {
if (!preg_match('/^(http:\/\/)?([^\/]+)/i', $url)) { $url='http://'.$url; }
$pr=getpr($url);
return $pr;
}
?>
Geolocalización en PHP5 usando GeoIP de Maxmind
11/07/2008
Una de las principales ventajas de la red Internet es el alcance de la misma, ya que es posible conectarse a ella y utilizar los servicios que ofrece desde prácticamente cualquier país del mundo. Esto, que puede parecer una enorme ventaja, puede también suponer un problema a la hora de analizar con detalle el tráfico recibido en un sitio web.
Para ayudarnos en esa tarea podemos utilizar una herramienta que nos permita identificar la localidad y/o el país desde el que se producen las visitas a nuestra web. De esta forma seremos capaces de llevar a cabo acciones como esta:
- Seleccionar automáticamente el idioma en el que se muestran los contenidos de la web en función del país de procedencia del visitante.
- Mostrar páginas personalizadas para visitantes de diferentes regiones geográficas.
- Preseleccionar automáticamente el país, provincia y ciudad de residencia en los formularios que precisen esa información.
Una base de datos de geolocalización es el elemento fundamental necesario para poder determinar la ubicación geográfica a partir de la dirección IP del visitante. Para el desarrollo de esta aplicación se ha utilizado la proporcionada por Maxmind que está localizable, junto con su API para PHP en la dirección
http://www.maxmind.com/app/php.
El siguiente ejemplo de código muestra el procedimiento a seguir para determinar la ubicación geográfica del visitante de la página, que pasa por la obtención de la dirección IP real del visitante (sin tener en cuenta proxyes), la carga de la base de datos de geolocalización y la llamada a la función que se encarga de convertir esa dirección IP en código de pais.
<?php
include_once('geoipcity.inc');
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$ip_address = $_SERVER["HTTP_X_FORWARDED_FOR"];
} elseif (isset($_SERVER["HTTP_CLIENT_IP"])) {
$ip_address = $_SERVER["HTTP_CLIENT_IP"];
} else {
$ip_address = $_SERVER["REMOTE_ADDR"];
}
$gi = geoip_open('GeoLiteCity.dat',GEOIP_STANDARD);
$record = geoip_record_by_addr($gi,$ip_address);
$pais = $record->country_code;
?>
Descarga de datos financieros en formato CVS
11/07/2008
Para el desarrollo del proyecto diariobolsa.com
http://www.diariobolsa.com, una plataforma de consulta de información bursátil en tiempo real, se ha utilizado un mecanismo que permite llevar a cabo la descarga de los datos referentes a índices y empresas desde Yahoo Finance.
Este mecanismo se apoya en la plataforma Open Populi para abstraer el acceso a las bases de datos en las que se almacena la información de cotización de las empresas e índices.
A continuación se muestra un ejemplo en el que se realiza la carga en la base de datos de los datos referentes a la cotización de las divisas.
<?php
function get_url($url) {
$curld = curl_init();
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_PORT, 80);
curl_setopt($curld, CURLOPT_VERBOSE, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER,1);
curl_setopt($curld, CURLOPT_TIMEOUT, 20);
$content = curl_exec($curld);
curl_close($curld);
return($content);
}
function __autoload($clase) {
$directorio = strtolower(str_replace('OP_','',$clase));
$fichero = "./$directorio/$clase.class.php";
include_once($fichero);
}
$fecha_actual = date('Y-m-d');
$db = OP_Database::factory('PgSql','pgsql:dbname=xx host=localhost','user','pass','');
$qrstr = "SELECT * FROM divisas WHERE divisa <> 'EUR'";
foreach ($db->executeQuery($qrstr) as $row) {
$divisa = trim($row['divisa']);
$url = 'http://es.old.finance.yahoo.com/d/quotes.csv?s=EUR'.$divisa.'=X&f=sl1d1t1c1ohgv&e=.csv';
$contenido = trim(get_url($url));
list($desc,$valor,$resto)= split(';',str_replace(',','.',$contenido));
$qrstr = "SELECT COUNT(*) AS total FROM historico_divisas WHERE ";
$qrstr .= "divisa='$divisa' AND fecha='$fecha_actual'";
$resultado_parcial = $db->executeQuery($qrstr);
$total = intval($resultado_parcial[0]['total']);
if ($total == 0) {
$qrstr = "INSERT INTO historico_divisas VALUES ";
$qrstr .= "('$divisa','$fecha_actual','$valor')"
$db->executePrepare($qrstr);
echo "$divisa:$fecha_actual:$valor\n";
} elseif ($total == 1) {
$qrstr = "UPDATE historico_divisas SET valor='$valor' WHERE ";
$qrstr .= "divisa='$divisa' AND fecha='$fecha_actual'";
$db->executePrepare($qrstr);
echo "$divisa:$fecha_actual:$valor (UPDATE)\n";
}
}
Las tablas en las que se apoya el script son la de divisas y el historico de divisas. La estructura de la primera de ellas es la siguiente:
- divisa character(20) : Contiene el identificador de la divisa (EUR, USD, GBP, etc.)
- descripcion character(150) : Contiene la descripción de cada divisa.
La estructura de la tabla historico_divisas es esta:
- divisa character(20) : Contiene el identificador de la divisa (EUR, USD, GBP, etc.)
- fecha timestamp : Fecha de actualización
- valor numeric(10,2) : Tasa de cambio frente al Euro
Contenidos de la tabla divisas:
divisa | descripcion
--------+-------------------
EUR | Euro
USD | Dólar USA
GBP | Libra Esterlina
JPY | Yen Japonés
CHF | Franco Suizo
CAD | Dólar Canadiense
AUD | Dólar Australiano
ARS | Peso Argentino
MXN | Peso Mexicano
BRL | Real Brasileño
Ejemplo de los contenidos de la tabla historico_divisas:
divisa | fecha | valor
--------+---------------------+--------
AUD | 2008-07-18 00:00:00 | 1.63
CAD | 2008-07-18 00:00:00 | 1.59
CHF | 2008-07-18 00:00:00 | 1.62
ARS | 2008-07-18 00:00:00 | 4.80
USD | 2008-07-18 00:00:00 | 1.59
JPY | 2008-07-18 00:00:00 | 168.33
MXN | 2008-07-18 00:00:00 | 16.22
GBP | 2008-07-18 00:00:00 | 0.79
BRL | 2008-07-18 00:00:00 | 2.54
CAD | 2008-07-17 00:00:00 | 1.60
BRL | 2008-07-17 00:00:00 | 2.52
ARS | 2008-07-17 00:00:00 | 4.80
MXN | 2008-07-17 00:00:00 | 16.23
GBP | 2008-07-17 00:00:00 | 0.79
JPY | 2008-07-17 00:00:00 | 168.50
CHF | 2008-07-17 00:00:00 | 1.62
USD | 2008-07-17 00:00:00 | 1.59
AUD | 2008-07-17 00:00:00 | 1.63
USD | 2008-07-16 00:00:00 | 1.58
AUD | 2008-07-16 00:00:00 | 1.62
CHF | 2008-07-16 00:00:00 | 1.61
CAD | 2008-07-16 00:00:00 | 1.59
JPY | 2008-07-16 00:00:00 | 166.25
BRL | 2008-07-16 00:00:00 | 2.53