Sunday 4 October 2009

class_SRR_database_sim :: mi base de datos en PHP

Hacía mucho tiempo que quería compartir con vosotros mi "clase de base de datos" que llevo trabajándome en PHP desde hace ya 3 años largos! La idea nació de la necesidad de almacenar información para una aplicación web de un amigo sin poder usar MySQL ni otro tipo de base de datos tradicional, porque su plan de hosting era el más básico :S

Por fin, después de este tiempo, la base de datos ya está más o menos madura como para compartirla con otros. Lo que más me ha costado es darle propiedades de no "corruptibilidad" por coincidencia de dos threads PHP sobre la misma tabla de datos. Es decir, que ahora por fin la base de datos bloquea el acceso de lectura/escritura cuando algún "thread" está ESCRIBIENDO sobre alguna tabla. De esta forma no puede ocurrir que alguien empiece a leer una tabla cuando alguien la tiene a "medio escribir". Lo cuál aunque parezca muy improbable, empieza a volverse probable en cuanto la base de datos aumenta de tamaño ;)

Queda mucho por hacer, así que la he colgado como proyecto opensource en INDEFERO. Aquí tenéis los enlaces:

[DEMO] http://imasdeweb.com/opensource/php_SRR_database_sim/demo

[HOMEPAGE] http://imasdeweb.indefero.net/p/class-SRR-database-sim

[DOC] http://imasdeweb.indefero.net/p/class-SRR-database-sim/doc

Sois programadores y ya sabéis que cualqueir software siempre es mejorable, pero en fin, si no publico nunca ninguna versión el progreso de la misma irá tan lento como hasta ahora. Y confío que ahora una vez que alguien empieza a descargarla y usarla pueda tener feedbacks constructivos :))


Resúmen de características

  • No requiere en el servidor más que tener PHP instalado
  • Para empezar a usarla solo hay que copiar el directorio de la base de datos en donde más te convenga y tantas "instancias" como quieras.
  • Ocupa muy poco. Estando vacía menos de 50kb.
  • Lleva un archivo admin3.php al que puedes llamar para gestionar tu base de datos de forma básica online (al estilo phpmyadmin pero muuuuuho más humilde, jejejeje).
  • Los registros de cada tabla se almacenan en un archivo de texto plano en el mismo directorio, con lo cuál solo necesitas darle permisos de escritura al directorio ;)
  • La clase ofrece toda una serie de sencillos métodos del tipo: crear/modificar/eliminar tabla y crear/modificar/seleccionar/eliminar registros
  • Los campos de cada tabla pueden contener ilimitados caracteres (olvídate por fin de definir tipo y longitud de campo).
  • Las consultas (o "queries") no se escriben como en SQL en una cadena de texto y un chingo de comillas, sino que son llamadas parametrizadas usando siempre arrays ;)
  • Hacer un backup o migrar a otro hosting es tan fácil y rápido como "copiar y pegar el directorio"
  • Como no es necesario ejecutar un segundo servidor (por ejemplo el de MySQL) sino solamente leer y escribir en archivos de texto plano desde el PHP, la velocidad de respuesta es rapidísima (comparada con MySQL) para tablas con menos de 5.000 registros

Cosas que no tiene

  • índices
  • vínculos entre tablas
  • (un montón de cosas... jejejeje)

En fin, espero que lo pruebes y me digas qué te pareció. Y bueno, si además eres programador... bueno, una ayudita estaría genial!!! Sinceramente, aunque nadie más ponga ni una línea de código en este proyecto, ya estoy muy contento de compartir por primera vez algo realmente mío :)))) le debo tanto a la comunidad de programadores en general... !!!! me siento bien retornando algo de mi parte.

Por cierto, esta clase de base de datos es la que utilizo como pilar de mi CMS, el cuál llevo construyendo y utilizando desde hace también 3-4 años! muy prontito también lo publicaré como opensource, porque considero que a alguien le puede ir bien. Un CMS más, verdad?! jajajaja... pero en fin.. no os lo creeréis, pero ninguno de los CMS que habían en aquel entonces me venían bien! o eran muy complicados para el programador (si necesitaba hacer cambios en la funcionalidad del mismo) o bien tenían un panel de administración bastante complejo para el nivel de mis clientes. Conclusión: acabé programando mi propio CMS. La ventaja: si necesito cualquier cambio lo tengo en un instante. Y si aparece algún bug, enseguida sé de dónde viene ;)

Websites funcionando que usan mi CMS y la susodicha base de datos

Para que veáis que tanto la base de datos como el CMS funcionan a las mil maravillas, aquí os dejo el enlace a unas cuantas de mis páginas que los usan. Y las dos primeras os puedo asegurar que tienen tablas con más de 1.000 registros!

Un saludo!!
SERGI


Actualizaciones posteriores
  • versión 3.2 (3-NOV-2009): se corrigió un error en el método de DELETE_TABLE, y se añadieron unos archivos (test.htm y test.php) para hacer benchmark de acceso simultaneo desde diferentes navegadores (solo para uso de desarrollo de la clase).

  • ...

  • versión 5.0 (17-FEB-2011): nuevas funciones de IMPORTACIÓN/EXPORTACIÓN de tablas (internamente, para cambios de versión de la clase o para migrar datos de un site a otro, etc); nuevas funciones de REGISTRO (o LOG) de escritura y/o lectura! (útil para debugeo de vuestra aplicación web: conocer qué operaciones se han estado realizando y cuando, y cuánto tiempo llevaron. Más adelante le añadiremos funciones de recuperación de datos)

  • para seguir la evolución del proyecto, mejor es visitar el LOG oficial del mismo: en INDEFERO

Saturday 19 September 2009

modem 3G Nokia CS-10 en Ubuntu

El primer modem 3G de Nokia fue el Nokia Internet Stick CS-10, y según ponen en sus especificaciones (http://europe.nokia.com/find-products/accessories/all-accessories/home-and-office/imaging/nokia-internet-stick-cs-10), no es compatible con Linux. Sin embargo, eso no es cierto: yo he podido instalarlo después de leer y buscar por internet un día entero, y hacer mil y una pruebas.

Atención: estos pasos de configuración no funcionan en Ubuntu 9.10, aunque sí en versiones anteriores... paradójicamente! Ando buscando solución para 9.10, pero si alguien ya la encontró o la encuentra... por favor, que haga el esfuerzo de comentar cómo lo consiguió o que deje el enlace en dónde encontró la solución ;)

Actualización 27 Nov-2011: otro amable lector -Alberto- nos ha dejado otra buena noticia:

"Ubuntu 11.04 con telcel no tienes que hacer nada solo entrar en preferencias -> Conexiones de red (network manager) y click en añadir, sigues los pasos con los valores por defecto seleccionando telcel y listo ya puedes ver en el icono de redes le das conectar y listo. "

La verdad es que yo -como usuario- hace ya más de un año que no uso el stick USB para conectarme. En realidad lo usé muy poco... jejeje, a pesar de todo el trabajo que me dió configurarlo!

Ahora mismo, hace 3 semanas, compré un Samsung Galaxy Ace -con Android- que creo que si le activo un plan de datos 3G puedo usarlo como "modem-router" wifi para mis Pcs/laptops!!! (tethering). Tengo que investigarlo, probarlo y publicar una guía si es así ;)

Actualización 30 Abr-2011: un amable lector nos ha dejado este mensaje que supongo todos los usuarios de Ubuntu celebramos

"El tema ya es viejo, las nuevas versiones de ubuntu 10.10 en adelante reconocen este dispositivo automaticamente, solo es cuestion de configurarlo.

Yo solo di click derecho en agregar conexiones, dispositivo nokia internet stick cs10, pais mexico, operador telcel, aceptar y listo."

Una gran noticia, no?


Sé que la temática de este artículo se sale de la habitual en mi blog (desarrollo web), así que espero que mis lectores habituales me perdonen por incluirlo, pero he visto imprescindible hacerlo porque me ha llevado muchas horas gastadas para hacer algo tan sencillo, y creo que debe ser compartido. Y este blog es mi principal "ventana al mundo" ;)

Principal problema: reconocer el stick como MODEM

El primer problema es que al introducir el modem USB en la PC es reconocido como unidad de almacenamiento de tipo CD-ROM, porque este Stick contiene una pequeña memoria fija de 80Mb con software para Windows.

Después de leer en innumerables blogs y foros las más variopintas formas de "evitar" que Ubuntu reconozca al stick como memoria en lugar de cómo modem, descubrí ACCIDENTALMENTE (sí, sí... accidentalmente) que la solución al problema es SENCILLÍSIMA:

1. introduce el stick en la ranura USB

2. ubuntu te lo reconoce como unidad de CD-ROM y lo monta

3. te habrá aparecido un icono en el Escritorio o en Nautilus que se llama "Nokia modem", pero que no muestra ningún archivo (curiosamente no se ven los archivos que contiene... cosa que no entiendo ¿? porque en Windows se ven sin problema)

4. haz clic sobre el icono con el botón secundario del mouse y escoge la opción "EXPULSAR".

5. "mágicamente" verás como la "unidad" es "desmontada" y PLIS: se enciende de forma intermitente una luz naranja en el modem, bingo !! tu modem acaba de ser reconocido y activado!

Es importante que destaque que no sirve solamente "desmontar" la unidad. Eso lo estuve haciendo repetidas veces pero no servía para que el modem fuera detectado. Esto no sucedió hasta que no escogí la opción "expulsar" (increible, no?).


Segundo problema: configurar la conexión con Telcel

Una vez reconocido el modem como tal, el segundo y último paso es configurar la conexión con Telcel. Lo cuál es sencillo para versiones de Ubuntu a partir de la 8.10 en adelante, porque el Network Manager lleva un asistente para conexiones de "banda ancha móvil".

Si tienes una versión de Ubuntu anterior a la 8.10 (en mi caso en mi netbook tenía Ubuntu Remix basado en la 8.04), tampoco debes preocuparte, solamente tienes que actualizar tu Network manager:

1. debes añadir a tus orígenes de software los siguientes repositorios:

deb http://ppa.launchpad.net/network-manager/ubuntu hardy main
deb-src http://ppa.launchpad.net/network-manager/ubuntu hardy main

puedes hacerlo editando directamente el archivo que contiene la lista de repositorios

sudo gedit /etc/apt/sources.list (usa este comando desde la consola)

o bien abrir la aplicación "Gestor de paquetes Synaptic" (En Sistema / Administración) y allí ir al menú Configuración / Repositorios y en la pestaña "Software de terceros" puedes "Añadir" nuevos repositorios.

2. añade también la clave pública para estos repositorios. Para ello ejecuta en la consola este comando:

sudo aptitude update

y te va a dar un error porque le falta la clave pública del repositorio que acabamos de añadir. Te has de fijar bien en el mensaje de error que te aparezca, que será del tipo:

W: GPG error: http://ppa.launchpad.net intrepid Release: Las firmas siguientes no se pudieron verificar porque su llave pública no está disponible: NO_PUBKEY 5DC4E17435661D98

entonces ejecuta este otro comando:

gpg --keyserver subkeys.pgp.net --recv NO_PUBKEY

en donde en lugar de NO_PUBKEY has de escribir tu clave (en mi ejemplo: 5DC4E17435661D98), y entonces la consola te devolverá algo como esto:

gpg: solicitando clave 35661D98 de hkp servidor subkeys.pgp.net
gpg: /home/ubuntu/.gnupg/trustdb.gpg: se ha creado base de datos de confianza
gpg: clave 35661D98: clave pública "Launchpad PPA for GNOME Do Testers" importada
gpg: Cantidad total procesada: 1
gpg: importadas: 1 (RSA: 1)

a continuación ejecuta este otro comando:

gpg --armor --export 5DC4E17435661D98 | sudo apt-key add -

(obviamente escribiendo tu NO_PUBKEY), y si todo va bien la consola te devolverá un "Ok!".


3. desde consola actualiza el paquete de tu network manager:

sudo aptitude install network-manager

4. una vez actualizado, cargar el "gestor de actualizaciones" desde el menú del Escritorio (Sistema / Administración), y te detectará que puede actualizar 3 paquetes relacionados con el network-manager, dos de ellos justamente relacionados con las conexiones móviles, como una base de datos con las características de los diferentes operadores telefónicos de una gran lista de países ;)

5. por último, aunque no es necesario, te recomiendo que reinicies la computadora


Bueno, así que suponiendo que ya tengas bien actualizado tu Network Manager, el resto es muuuuy fácil:

1. haz clic con el botón secundario del ratón sobre del pequeño icono del gestor de redes que hay en tu barra (panel systray) junto al reloj y la fecha (normalmente).

2. escoge la opción "editar las conexiones"

3. ves a la pestaña "Banda ancha móvil" y haz clic en el botón "Añadir"

4. si tienes la versión actualizada del network manager se habrá iniciado un "asistente" que te va a guiar en el proceso de creación de la conexión móvil a internet. Además te va a preguntar por el país y te va a mostrar los operadores telefónicos para los que tiene datos en ese país. Tú deja todas las opciones de conexión que ponga el asistente, no hace falta que toques nada (al menos en el caso de Telcel-México). Como mucho, marca la casilla que pone: "Conectar automáticamente", porque así Ubuntu se conectará a esa red móvil en cuanto enchufes el modem USB 3G ;)

5. ya solo tienes que encajar tu stick en la ranura USB y tiene que funcionar! en todo caso, si ya lo tenías puesto, tendrás tal vez que retirarlo y volver a seguir los pasos del inicio ("expulsarlo" una vez sea reconocido por ubuntu como CD-ROM). O si eso no te va bien, mejor reinicia de nuevo la compu :( he experimentado que si introduzco y extraigo el modem USB más de una vez en una misma sesión, no siempre es reconocido adecuadamente ;)

Bueno, espero que estos consejos hayan servido a más de uno... espero que a muchos!!! lo más chistoso del tema es que acabo de recibir la respuesta del servicio de apoyo al cliente de Nokia (les pregunté sobre cómo usar sus modems USB en Linux), y lo único que dicen (me han respondido 2 personas diferentes) es la "ODIOSA" letanía de "Le recordamos que tal como puede leer en la página web de especificaciones del CS-10, este equipo solo es compatible con el sistema operativo Windows o Mac Os". ¿Increible, no? En fin, una vez publicado esta breve guía se la voy a enviar en respuesta a su "estúpida" respuesta de "ayuda". Porque además, cuando les escribí pidiendo ayuda les explicité que lo único que necesito es que me dieran un link en donde poder leer ALGO de documentación de como instalar en Linux alguno de sus modems USB 3G que SÍ SON COMPATIBLES CON LINUX (a partir de la CS-15). En fin...

Por cierto, si alguno/a de vosotros no conoce aún Ubuntu... le recomiendo que lo pruebe! lo recomiendo como usuario y como programador. Como veis, es un software totalmente ya maduro para el trabajo de los profesionales de la informática ;)

Un saludo,
SERGI

Monday 6 July 2009

Di NO a las PATENTES DE SOFTWARE

Actualmente (2008-2009) se está desarrollando una campaña de recogida de firmas para solicitar al Parlamento Europeo medidas más contundentes que erradiquen de una vez por todas las patentes de software en Europa. Te invito a que te informes de porqué este cambio nos conviene no sólo a los informáticos, sino también a la industria del software en general y de forma indirecta a toda la sociedad. Mientras que las patentes sólo benefician el "lucro" a corto plazo de un número contado de empresas y retrasan enormemente la competitividad y el progreso del sector informático, y de rebote de toda la sociedad!



Te invito pues a:



  • Visitar la página oficial de recogida de firmas.

  • Dejar allí tu firma y leer alguno de los estudios científicos que hay en torno al tema

  • Divulgar este asunto, colocando un banner en algunas de tus webs ;)

  • Incluir un artículo en tu blog



Banners


Para que lo tengas más fácil, aquí te dejo el código para insertar un banner horizontal y otro vertical:


<a target="_blank" href="http://petition.stopsoftwarepatents.eu/211002065663/"><img src="http://stopsoftwarepatents.eu/media/ssp-732-121.gif" alt="stopsoftwarepatents.eu petition banner" width="580" height="96" style="border:none;" /></a>


stopsoftwarepatents.eu petition banner


<a target="_blank" href="http://petition.stopsoftwarepatents.eu/211002065663/"><img src="http://petition.stopsoftwarepatents.eu/banner/211002065663/ssp-240-400.gif" alt="stopsoftwarepatents.eu petition banner" width="240" height="400" style="border:none;" /></a>


stopsoftwarepatents.eu petition banner

Monday 8 June 2009

Comillas dobles y simples en HTML "input text"

Si eres desarrollador de aplicaciones web, sabrás ya que el tema de las comillas (simples y dobles) es siempre una fuente de problemas hasta que no descubres cómo lidiar con ellas. Voy a resumir ahora cuatro cosas que te van a funcionar siempre ;) si trabajas con PHP.

El problema principal es cuando queremos definir una cadena de texto que contenga unas comillas. Es un problema en sí mismo porque las cadenas de texto generalmente se delimitan usando comillas.

Uno pensaría que sería posible insertar comillas en una cadena de texto "escapando" esas comillas, es decir, usando un símbolo de "escape" (usualmente es la barra invertida \) delante de las comillas a escapar. Por ejemplo:

<input type='text' value='L\'Hospitalet' />

o bien:

<input type="text" value="un \"gran\" hombre" />

sin embargo, si haces la prueba, verás que ninguno de estos "escapes" funciona !!!!

Para incluir comillas dobles dentro de comillas dobles en HTML, tenemos que usar "carácteres especiales", así:

<input type="text" value="un &quot;gran&quot; hombre" />

La última cuestión es cómo hacer todo esto de manera fácil en PHP. Esto sería un ejemplo de cómo generar desde PHP el código HTML anterior suponiendo que la variable $value contenga una cadena con comillas dobles como un "gran" hombre:

<?php

echo "<input type=\"text\" value=\"".htmlspecialchars($value)."\" />";

?>

Como puedes suponer, la función htmlspecialchars transforma las comillas dobles (") en el código &quot;.

De hecho, htmlspecialchars transforma no solo las comillas dobles sino este pequeño grupo de carácteres:

  • [&] (ampersand) se transforma en [&amp;]

  • ["] (comillas dobles) se transforman en [&quot;] cuando no se usa ENT_NOQUOTES como segundo parámetro (opcional!).

  • ['] (comilla simple) se transforma en [&#039;] solamente cuando se usa ENT_QUOTES

  • [<] (menor que) se transforma en [&lt;]

  • [>] (mayor que) se transforma en [&gt;]



No olvides que de una forma u otra, cuando se envían por POST variables que contienen comillas (dobles o sencillas) estás llegan escapadas con una barra
diagonal delante [\] ("slash" en inglés) y que eliminamos con el PHP usando la función stripslashes() a cada variable $_POST() que pudiera contener comillas
de algún tipo!

En este enlace podéis ver un ejemplo de cómo usar htmlspecialchars() y lo explicado
en este artículo. Jugad con diferentes carácteres y mirad el código HTML de la página antes y después de enviar datos.

De todas formas:
  • posiblemente se me escapa algo, pues continuamente salen cosas nuevas o uno descubre nuevos "trucos"
  • existen artículos mucho más extendidos y con más ejemplos. Éste me gustó particularmente para gente que se incia en PHP, pues da un repaso más a fondo del uso de comillas en este lenguaje de programación: http://www.dxpro.es/index.php?topic=181.0
  • si detectas que he cometido algún error en mis ejemplos o en mi explicación, please, comenta aquí abajo y lo corregiré para ayuda de otros :)) Gracias!
Un saludo!
SERGI

Wednesday 27 May 2009

Cargar javascript desde JSON o AJAX con eval() en FF y execScript() en IE

¿Estás desarrollando una aplicación web en la que usas JSON o AJAX para cargar dinámicamente datos? ¿Además necesitas cargar dinámicamente no solamente datos sino también código javascript para ser utilizado en la página? A continuación te expongo el modo de hacerlo para que funcione en FireFox 3.X y IE7 (casualmente, también funciona en IE6).

Voy a partir de la base de que utilizas jQuery para la consulta JSON o AJAX al servidor. Y me voy a centrar en la "carga" del código javascript. Pero obviamente, no importa la librería o código que uses para el JSON o AJAX. Hay muchísima literatura en internet sobre ese tema.

Ejemplo de carga de datos

Empezaré con un ejemplo de carga de datos tipo texto, que es lo más habitual. Y luego pondré un ejemplo de carga de código javascript.


function js_actualizar_top_webs(){
var url_json = 'json_top_webs.php?mes='+$('#select_mes').val();
$.getJSON(
url_json,
function(datos){
$('#div_top_webs').html(datos.table_webs);
}
);
}

Imaginemos que en nuestra web de ejemplo tenemos un control tipo lista desplegable (select) que permite al usuario escoger un mes del año, y queremos que nuestra página recargue un <div id="'div_top_webs'"></div> con la lista del top 10 de webs de ese mes. Es un ejemplo clásico de recarga de datos dinámica usando JSON o AJAX.

Para ello construimos una función llamada js_actualizar_top_webs, que llamaremos al cambiar el valor de ese select. Esta función tal como está definida en el código de ejemplo hace una llamada JSON mediante el objeto $ de jQuery y cuando los datos se hayan recibido por completo ejecutará el código en rojo, en dónde "datos" es un objeto de javascript (no un array!!) que contiene diferentes "propiedades" con los datos enviados. En este caso, suponemos que el archivo de PHP que ha sido llamado devuelve un string llamado "table_webs" que contiene el código HTML de un "<table>" con la lista de webs que queremos mostrar.

Para "obtener" el valor de ese string HTML simplemente llamamos a datos.table_webs y lo colocamos como "contenido" de "div_top_webs" mediante el método .html() de jQuery. ¿Sencillo, no?


Ejemplo de carga de código javascript


Bueno, vamos ahora a algo que "cuesta" un poquitín más. Bueno, de hecho a mí me ha hecho gastar casi 8 horas de cansada búsqueda por internet y de leer mucho y probar mucho, hasta llegar a lo que te voy a poner aquí! ;)

Abajo tienes el código anterior ligeramente cambiado: hemos añadido unas líneas de código más, con el fin de poder cargar y ejecutar un código de javascript elaborado en el lado servidor.



function js_actualizar_top_webs(){
var url_json = 'json_top_webs.php?mes='+$('#select_mes').val();
$.getJSON(
url_json,
function(datos){
$('#div_top_webs').html(datos.table_webs);
if (window.execScript) window.execScript(datos.script_leyenda);
else window.eval(datos.script_leyenda);
}
);
}

Puedes ver en rojo dos nuevas líneas que corresponden a una sentencia condicional, y esto es necesario para ejecutar un código u otro según estemos en FireFox o en Internet Explorer (como mínimo estos dos navegadores). Para ambos casos el código de javascript pasado es el mismo y lo recuperamos de nuevo mediante jQuery con la propiedad datos.script_leyenda (que habremos rellenado desde el PHP con el código javascript que necesitemos ejecutar en nuestra web).

La diferencia radica en que Internet Explorer ejecutará el código de javascript mediante el método window.execScript(), mientras que FireFox ejecutará el código al usar el método window.eval(), en ambos casos pasando como argumento el código javascript que queremos ejecutar.

Otro aspecto importante y que tal vez pase desapercibido a primera vista es que en ambos casos ejecutamos sendos métodos sobre el objeto window. Eso es fundamental (me llevó más de una hora leyendo descubrirlo), para que el código javascript a ejecutar pase a estar "disponible" desde otros "ámbitos" de la misma página. De esta forma, si por ejemplo, dentro de ese código cargado dinámicamente definimos una función llamada "js_mostrar_leyenda()", si queremos usarla en el resto de la página, deberemos llamarla así: window.js_mostrar_leyenda();

Estoy seguro de que a más de uno le va a ir de perlas estas 3 líneas de código! al menos a mí me va a dar mucha potencia de programación con JSON y javascript. Y sinceramente, cuesta encontrar información "actualizada" en internet. Ya sabes que este tipo de información técnica (especialmente cross-browser) se desfasa con facilidad, con la salida de nuevas versiones de navegadores continuamente!! Así que lo que es válido hoy deja de serlo al cabo de 2 años :(

Te invito a que comentes cualquier sugerencia que tengas al respecto, eh! seguro que a todos nos interesa (temas de compatibilidad, por ejemplo). Si probando el código ves que te funciona en otros navegadores, por favor, coméntalo brevemente ;)


Actualización 2012-mar-06

Bajo petición de un comentarista añado a continuación un ejemplo de cómo debería ser el código PHP del archivo

json_top_webs.php 

que uso en los ejemplos de arriba.

1  <?php
2 
3       $ret
=array(
4                 
'table_webs'=>'<p>table</p>',
5                 
'script_leyenda'=>"alert('Hi world');"
6                 
);
7                
8       echo 
json_encode($ret);
9 
10  
?>

No necesita mucha explicación, pero por los que empiezan con el PHP:
  • definimos un array con dos elementos que llevan el nombre que luego usaremos en el javascript (como "datos.table_webs").
  • fijaros en el valor de cada uno de ellos. El primero es código HTML tal cuál, y el segundo es CUALQUIER instrucción o instrucciones de javascript, desde un simple alert() hasta definir funciones si es necesario.
  • por último hacemos un echo del array pasándolo primero por la función nativa de PHP json_encode() que lo que hace es reescribir los datos en formato JSON (ya sabéis: con llaves, comillas dobles, y comas, escapando los valores que haga falta). Cuidado porqué esta función creo que no estaba en algunas versiones de PHP 4.x ! pero en fin, quiero pensar que hace años que todo el mundo actualizó ya a PHP 5.x

Un saludo!!!
SERGI

Thursday 21 May 2009

FireFox, solución a "Ha escogido abrir..."

Este artículo es un poco técnico, pero me veo obligado a publicarlo para compartir la solución a un problema que creo que es más común de lo que parece y sobre el que he encontrado muy poco escrito. Pienso que los desarrolladores de aplicaciones web que lo lean me lo agradecerán. Tal vez no te haya ocurrido nunca, pero ya llegará el día, jejejeje...

Signos del problema

- Cuando se intenta acceder a ciertas páginas de la aplicación, el navegador tarda en responder como casi un minuto y luego -al menos en FireFox- aparece una ventana en la que el mensaje dice "Ha escogido abrir este archivo..." y te da la opción de guardar el "supuesto" archivo PHP en lugar de mostrar el contenido de la web. Si pruebas a visitar la misma página desde IE la página quedará completamente en blanco y no mostrará nada.

- Es posible que algunas páginas de la web si vayan, porque sean HTML, pero también algunas de PHP también pueden verse mientras otras no. Después de leer el artículo entero entenderás porqué.

- El problema te ocurre tanto si visitas las web desde tu PC o desde cualquier otro. Aunque ten cuidado con las copias guardadas en caché!!! te pueden jugar "bromas pesadas": hacerte creer que una página se ve bien cuando realmente no es así, o al revés! Para estar seguro siempre de que el navegador te muestra la página devuelta por el servidor te aconsejo refrescar la página con CTRL+F5.

Causa del problema

- Tu aplicación web está teniendo algún "problema" en algún punto de sus "cálculos": en algún lugar del código se entra en una especie de "bucle infinito" que deja a la aplicación "pensando indefinidamente". Por esta causa el servidor da una respuesta "incoherente" al navegador, o digamos que el navegador no sabe interpretar la respuesta (¿curioso, verdad?).

- Por ejemplo, en mi caso se trataba de una clase que yo había programado para acceder a un archivo histórico, que cuando accede bloquea/desbloquea un archivo como señal de alerta para otros "hilos" de la aplicación, de que no pueden acceder en ese momento a la aplicación. La cuestión es que este sistema me funciona sin problemas (hasta ahora). Pero la empresa de hosting hizo una especie de restauración de backup en los servidores que debieron alterar los permisos de escritura sobre el directorio en el que está ubicado el archivo. En conclusión: mi aplicación se quedaba encallada en ese punto: intentando acceder a ese archivo permanentemente "bloqueado". Una vez que descubrí este fenómeno, la solución fue fácil: por FTP cambié los atributos del directorio para poderse escribir en él!

- Sin embargo, mi caso es muy particular y entiendo que difícil de repetir por otros programadores. Pero no así el problema de fondo: dejar a la aplicación en "stand by" (o en bucle) hasta resolver algo irresoluble. Por ejemplo, entre los comentarios que he leído en otros foros, hay gente que le ha pasado exactamente lo mismo cuando su aplicación web intenta hacer un "fopen", que es una lectura de un archivo remoto con PHP. Lo que quiero decir es que si no te ha pasado todavía (como desarrollador web) es muy posible que te pueda pasar en breve. Sobretodo hoy en día en el que la programación con webservices y consultas remotas está tan a la orden del día ;)

- [Nota añadida 8-ago-2009] Hoy me ha ocurrido otra vez un bucle de esos que me ocasionó el mismo error! Aunque esta vez, sabiendo que se trataba de un bucle infinito he ido haciendo "debugeo" hasta encontrar donde se me "colapsaba" la aplicación ;) El bucle era del tipo "función recursiva MAL HECHA":

function mi_funcion(){
if (condicion)
return mi_funcion();
else
return 'a otra cosa mariposa';
}


Con lo cuál, en los casos en que "condición" es verdadero se llama de nuevo a la misma función, en un bucle infinito!! ¿Cuál fue esta vez mi error...? pues que en realidad cuando escribí el código quería llamar a una "mi_segunda_funcion()" en el caso de que "condicion" fuera verdadero... en fin, que me equivoqué con el nombre de la función!!! :((( es uno de esos errores del "copiar y pegar", del que no hace falta que diga mucho más, verdad?! jejeje...

Bueno, en fin, cualquier comentario al respecto será bienvenido, si te ha pasado lo mismo o algo parecido. No dudes en comentarlo aquí... igual que tú has llegado hasta aquí con ese problema, detrás tuyo vendrá más gente a quien le podrá ser útil! no olvides escribir tu solución cuando la halles! ;)

Un saludo!
SERGI
PD: está lloviendo!!! jajajaja... se agradece en este tórrido territorio (desierto de Sonora, México) en donde solo ayer estabamos a 43 grados (a finales de Mayo...).

Friday 10 April 2009

Redirección interna con RewriteRule, RewriteCond y paso de variables PHP

Llevo 5 horas leyendo, buscando, probando... y por fin hallé lo que necesitaba!!!!!!!! Lo más frustante ha sido encontrar apenas casos como el mío... lo cuál no acabo de entender pues creo que es de lo más normal.

Mi caso es el siguiente: tengo un site funcionando en inglés

http://midominio.com

y como es un CMS hecho en PHP tengo URLS del tipo:

http://midominio.com/index.php?pag=foro

Lo que necesito es convertir mi CMS en multi-idiomas con los menos cambios posibles y de una forma amigable para usuarios y buscadores. ¿Creo que es algo muy generalizado, verdad? Por eso no entiendo el porque me ha costado encontrar referencias que me ayudaran. Tal vez he estado buscando mal ;)

La solución que buscaba era la de poder tener URLS como

http://midominio.com/es/index.php?pag=foro

que llevara "internamente" (es decir, sin que el visitante se dé cuenta... es decir, sin que camb ie la URL en su navegador) a esta otra URL

http://midominio.com/index.php?pag=foro&idioma=es

De esta forma el visitante podría estar navegando siempre por el "subdominio" /es/ pero en realidad la aplicación de PHP que es llamada es siempre la misma solo que se le pasa la variable idioma y de esta forma puede mostrar los contenidos adecuados! ;)

La última condición que necesitaba cumplir es que el archivo PHP llamado fuera cualquiera y no solo index.php, y que las variables de la URL fueran cualesquiera y cuantas sean necesarias.

Y en fin, después de 5 horas de lecturas y pruebas hallé el contenido del archivo .htaccess que tengo que poner en la raiz de ese dominio:

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^es/$ $1?idioma=es
RewriteRule ^es$ $1?idioma=es
RewriteCond %{QUERY_STRING} ^([^/]+)
RewriteRule ^es/(.+)\.php$ $1.php?%1&idioma=es
RewriteRule ^es/(.+)$ $1

No quiero hacer un tutorial aquí del tema, porque sería larguísimo y además no tengo los conocimientos adecuados. Posiblemente incluso la sintaxis que he utilizado podría mejorarse. Pero voy a detallar lo poco que he entendido de estas líneas de código y su función:
  • las dos primeras líneas son necesarias sí o sí si se quieren utilizar en APACHE estas reglas de redirección
  • la 3a y 4a líneas son para añadir "?idioma=es" a la URL cuando no menciona ningún archivo PHP: http://midominio.com/es
  • las líneas 5a y 6a van "ligadas". La primera "lee" el QUERY_STRING que son las variables $_GET que vienen después del "archivo.php?", y las insertamos en el output de la línea 6a como %1.
  • la línea 7 es para que se redireccionen todas las demás URLS, esta vez sin añadir nada del idioma, como las imágenes o las hojas de estilo CSS.
Para el que quiera saber más, decir que las sentencias RewriteRule manejan siempre la sintaxis:

RewriteRule input output

En donde input es una "expresión regular" (expresión que usa un lenguaje de comodines) que coincidirá solamente con algunas URLs concretas, y output es simplemente las URLS que finalmente debe construir y llamar Apache, utilizando normalmente "partes" del input. Esas partes son $1,$2,$3... (tal como se utilizan en la sintaxis del output), y se corresponden con el contenido de los diferentes paréntesis () que hay en la expresión regular del input.

Ejemplo:

RewriteRule ^es/(.+)\.php$ $1.php?%1&idioma=es

coincidirá con URLS del tipo:

es/index.php?pag=foro (pag=foro sería el QUERY_STRING,%1)
y será transformada en index.php?pag=foro&idioma=es
Como véis, el nombre del archivo php (index) es una variable! ;)
y el QUERY_STRING también aunque se lee en una línea RewriteCond previa.

La verdad es que el tema es un auténtico lío, jejeje... así que no dudo de que alguno de vosotros no haya entendido la mitad! Lo que si puedo aseguraros es que si queréis entender algo deberíais empezar por leer algún tutorial sobre el tema de .htaccess o de las expresiones regulares, que es de lo que va esto ;)




Actualización 6 Enero 2010

Podemos simplificar aún más el trabajo con idiomas

Trabajando esta semana en el tema de los idiomas (de nuevo) he descubierto que se puede conseguir lo mismo que pretendíamos al inicio de este artículo pero de manera un poco más "limpia" y cómoda. Me explico.

Arriba hemos utilizado el archivo .htaccess para realizar una redirección interna de:

http://midominio.com/es/index.php?pag=foro

a

http://midominio.com/index.php?pag=foro&idioma=es


Y sin embargo he descubierto que podemos optar a una redirección más sencilla (con menos líneas y menos complicación en el archivo .htaccess) si usamos bien el PHP ;) En concreto, la redirección la llevaremos simplemente hacia:

http://midominio.com/index.php?pag=foro

Es decir, que literalmente nos "comemos" el "es/" (más abajo pongo como ha de ser el .htaccess), y usamos el PHP para "leer" el "idioma" (en nuestro ejemplo 'es') en la URL (a la que accedemos con $_SERVER['REQUEST_URI']). El código PHP sería algo como esto:

$a_idiomas=array('es','en','fr');
$REQUEST_URI=$_SERVER['REQUEST_URI'];
foreach($a_idiomas as $v){
    if ( !strpos($REQUEST_URI,'/'.$v.'/')===false
       or substr($REQUEST_URI,-1*(strlen($v)+1))=='/'.$v )
          $idioma=$v;
}


En el array $a_idiomas cargamos las siglas de los idiomas que usamos en nuestro site. En $REQUEST_URI cargamos la URL solicitada (sin incluir el dominio o host), es decir, en nuestro ejemplo sería /es/index.php?pag=foro. Después, solamente ejecutamos un bucle que recorre cada uno de los idiomas buscando si sus siglas aparecen como /es/ o como /es (en una URL del tipo http://midominio.com/es). Si ocurre alguno de estos casos, entonces ya hemos encontrado cuál es el idioma que el visitante está requiriendo ;)

Fijaros que no hemos necesitado que la redirección interna nos lleve a una URL que contenga la variable GET &idioma=es. Y eso se va a traducir en lo siguiente: un archivo .htaccess mucho más limpio:

Options +FollowSymLinks
RewriteEngine On

RewriteRule ^es$ $1
RewriteRule ^es/$ $1
RewriteRule ^es/(.+)$ $1


Es decir, de 5 reglas nos hemos quedado con solo 3, y además las más sencillas! No entiendo muy bien la razón pero no he podido deshacerme de una de ellas. Intuyo que en realidad las 2 últimas RewriteRule son redundantes, pero después de hacer pruebas he comprobado que ambas son necesarias :S

Pero no se vayan... aún hay más! jejeje

Cuando no trabajamos en la raíz del dominio

Uno de los comentaristas (Carlos) preguntó acerca de trabajar en "local", en dónde lo anterior sufre algunos problemillas... :S En mi caso, también tuve problemas cuando quise trabajar en un directorio por debajo de la raíz del dominio, como por ejemplo en:

http://midominio.com/sports/es/index.php

Para no extenderme mucho, la solución que me ha funcionado ha sido usar la cláusula RewriteBase /directorio que indica al servidor Apache que si usamos redirecciones absolutas queremos que la "raíz" no sea la del dominio (http://midominio.com/), sino http://midominio.com/directorio. Esta cláusula nos será útil y necesaria solo si usamos el parámetro [R] al final de algún RewriteRule. Este parámetro obliga al servidor a CAMBIAR la URL que vino del navegador por la resultante de aplicar la regla Rewrite !!!

Para aclararnos, veamos como yo he dejado mi archivo .htaccess que he colocado en /sports:

Options +FollowSymLinks
RewriteEngine On

RewriteBase /sports

RewriteRule ^es$ es/ [R]
RewriteRule ^es/$ $1
RewriteRule ^es/(.+)$ $1


Añadir a lo dicho, que ahora la primera regla ya no es una redirección INTERNA, sino EXTERNA, es decir, al llegar a ella, si la URL pedida por el visitante cumple con la expresión ^es$ entonces se va a modificar la URL del navegador del visitante por una URL exactamente igual pero acabada en 'slash' /.

¿Para qué complicarnos tanto la vida? Pues porque de esta forma si alguien escribe:

http://midominio.com/sports/es

será redirigido a:

http://midominio.com/sports/es/

y con esto los enlaces relativos que hayan en esa página serán considerados por el navegador como relativos al directorio /es/ !!!! de otra forma serían considerados como relativos al directorio /sports/.

Bueno, como pueden ver... este tema es más complejo de lo que aparenta. Y me da la sensación de que hay muchos casos diferentes, y que han de buscar en su caso qué es lo que funciona. Les recomiendo que prueben y prueben... el "ensayo y error" también sirve ;)

Aquí tienen dos enalces por si necesitan leer más, del sitio oficial de Apache:



Una última aclaración: mod_rewrite es el módulo de Apache que usa el .htaccess, y está activado por defecto diría que en casi cualquier servidor de hosting Linux+Apache.

Suerte!
SERGI