Archive

Archive for the ‘php’ Category

LightVC: Un framework mínimo de vistas y controladores para PHP5

June 19th, 2011 3 comments

En estos días tenía que hacer un sitio web muy básico, estático, y para no repetir líneas y líneas de HTML le iba a meter algo de PHP. Pero no me daba hacer como en los viejos tiempos metiendo includes de header y footer, porque además quería poder manejar URLs un poco más amigables. No me iba a poner a reinventar la rueda para este pequeño sitio ni tenía ganas de usar un framework grande que pese 500 veces más que el sitio en sí para darme un montón de funcionalidades que no iba a usar.

Buscando un rato en Google me topé con LightVC. Es un framework muy básico y livianito que solamente implementa las vistas y los controladores de lo que sería un patrón MVC. Precisamente está pensado para sitios que, o bien no necesitan interactuar con una DB (como en mi caso) o que quieren integrarse fácilmente con un ORM.

Usarlo es muy sencillo. Simplemente nos bajamos del sitio web el esqueleto de una aplicación que zippeado pesa sólo 24 KB, lo descomprimimos, apuntamos nuestro virtual host a la carpeta webroot de nuestro proyecto y ya tenemos un ejemplo andando.

Luego tenemos dos archivos de configuración: config/application.php con algunas constantes e includes básicos y config/routes.php donde definimos un array para el ruteo de los requests. Les muestro el ejemplo que viene al desargar el framework que es bastante claro:

 parseInfo
$regexRoutes = array(

	// Map nothing to the home page.
	'#^$#' => array(
		'controller' => 'page',
		'action' => 'view',
		'action_params' => array(
			'page_name' => 'home',
		),
	),

	// Allow direct access to all pages via a "/page/page_name" URL.
	'#^page/(.*)$#' => array(
		'controller' => 'page',
		'action' => 'view',
		'action_params' => array(
			'page_name' => 1,
		),
	),

	// Map controler/action/params
	'#^([^/]+)/([^/]+)/?(.*)$#' => array(
		'controller' => 1,
		'action' => 2,
		'additional_params' => 3,
	),

	// Map controllers to a default action (not needed if you use the
	// Lvc_Config static setters for default controller name, action
	// name, and action params.)
	'#^([^/]+)/?$#' => array(
		'controller' => 1,
		'action' => 'index',
	),

);
?>

Luego tenemos una carpeta views y una carpeta controllers donde podremos poner nuestras vistas y controladores respectivamente. El esqueleto de la aplicación ya viene con ejemplos de todo esto, lo cual hace muy sencillo comprender cómo funcionan las cosas y dejar nuestra aplicación funcionando en forma muy rápida.

Así que ya saben, la próxima vez que tengan que hacer un sitio muy básico y quieran utilizar un framework muy sencillo que les ahorre trabajo en algunas tareas básicas, peguenle una mirada a LightVC.

Consejos de timezone by Demian Rodriguez

June 2nd, 2011 2 comments

time zone, el gran problema de la programacion. Consejos y trucos

Timezone, consejos y trucos para php, mysql y nodejs

Demian Rodriguez nos da unos consejos para minimizar estos problemas.

1) Establecer como timezone default “UTC” tanto en el server como en la base de datos.
Ej en Debian:

#dpkg-reconfigure tzdata
#restart mysql

2) Para el caso de MySQL, las columnas de tipo DATE, DATETIME o TIME no almacenan info sobre el timezone, uno es responsable de saber en que formato esta la fecha para despues mostrarla correctamente al usuario. Para esto es recomendable guardar todas las fechas en UTC.

3) Al mostrar una fecha al usuario se debe convertir al timezone adecuado. Como?

PHP:

// mostrar
$fechaDeMysql = "2011-06-01 08:30:20";
$date = new DateTime($fechaDeMysql, new DateTimeZone("UTC"));
$date->setTimeZone(date_default_timezone_get());
$date->format(...);

// guardar
$fechaQueVieneDeNoseDonde = 'Mon, 15 Aug 2005 15:52:01 +0300'; // ya tiene timezone
$date = new DateTime($fechaQueVieneDeNoseDonde);
$date->setTimeZone(new DateTimeZone("UTC"));
// guardarla en mysql...

NodeJS:
Si aca no mostramos nada al usuario es re facil, no hay que hacer nada :)
El proceso usa el tz del SO. Pero tambien lo podemos forzar seteando la variable de entorno al principio del script:

process.env.TZ = 'UTC';

Como obtener una fecha de la db y mostrarla al usuario en el TZ correcto? No se, es una incógnita como sabe JS en que TZ está la fecha que saque de la db. Lo pregunté aca: https://github.com/felixge/node-mysql/pull/55

// guardar
/**
 * metodo re copado para formatear fechas :)
 * return formatted date as yyyy-mm-dd H:i:s using UTC
 */
Date.prototype.formatUTC = function() {
	function pad(n) {
		return n < 10 ? '0' + n : n
	}
	var d = this;
	return d.getUTCFullYear()+'-'
	  + pad(d.getUTCMonth()+1)+'-'
	  + pad(d.getUTCDate())+' '
	  + pad(d.getUTCHours())+':'
	  + pad(d.getUTCMinutes())+':'
	  + pad(d.getUTCSeconds());
};

var fechaParaInsertar = new Date('Mon, 15 Aug 2005 15:52:01 +0300').formatUTC();

Configurar nuestra computadora como servidor web con ubuntu

April 21st, 2011 5 comments
entorno lamp

entorno lamp

Normalmente el enfoque de la web and beer esta orientado a personas que ya tienen un cierto nivel y pueden hacer estas cosas con los ojos cerrados y varios whisky’s encima, pero estamos haciendo un esfuerzo por captar nuevos miembros, aunque estos tengan un nivel muy basico.

La idea es clara, que todos podamos aprender.

En esta ocacion vamos a configurar nuestra computadora con Ubuntu para que pueda ser un servidor web y gestionar los distintos proyectos que podamos tener.

Esto va a ser una explicacion muy orientada a la practica, esto quiere decir que no profundizaremos mucho en ciertos temas y cuestiones, para ellos por favor buscar en otras fuentes, o quizas en alguno de nuestros post (seguro algo de informacion existe).

Para poder seguir este tutorial, por favor contar con una computadora con Ubuntu instalado.

Primero que nada, vamos a instalar Apache, PHP y MySQL. Esa combinacion de software es la mas tipica, y es conocida como entorno LAMP (Linux, Apache, MySQL y PHP).

Primero instalamos Apache:

sudo apt-get install apache2.2-common

El que no sepa que hace apt-get por favor ni se moleste en seguir con este tutorial!. Bueno bueno, es el manejar de paquetes, los paquetes son “cosas” como programas, librerias, etc. Ejecutando ese comando instalara apache 2.2 mas todas las dependencias (cosas que necesita para poder funcionar).

Una vez instalado apache instalamos PHP.

sudo apt-get install php5 php5-cli php5-common php5-curl php5-gd libapache2-mod-php5filter

Con eso deberia ser suficiente para nosotros, ojo, no intalamos aun el soporte a mysql, hay razones para esto que aprenderan con el tiempo y la experiencia ganada.

Ahora vamos a instalar MySQL.

sudo apt-get install mysql-server-5.1 mysql-client-5.1

Eso nos instalara el server y client (el programa para poder acceder a mysql).

Muy bien, ya tenemos todo entonces verdad? apache, mysql y php, pero ahora vamos por el detalle, la lib de acceso a mysql para php.

sudo apt-get install php5-mysql

Con eso ya tenemos todo listo para comenzar.

Ahora bien, primero vamos a aprender a iniciar, parar y reiniciar nuestro apache (es un proceso en memoria, se lo conocemo como daemon, hay muchos daemon en nuestro ubuntu).

Los daemons o programas que estan “a la escucha” en Ubuntu son manejados por un comento que casi siempre estan en:

/etc/init.d/<algo a manejar>

Por ejemplo, para iniciar apache escribimos

/etc/init.d/apache2 start

Para detener apache

/etc/init.d/apache2 stop

Para reiniciar (por ejemplo porque cambiamos algo de la configuracion de apache o PHP).

/etc/init.d/apache2 restart

Ahora bien, vamos a ver si todo esta funcionando correctamente.

Abrimos nuestro navegador (espero que sea un chrome a estas alturas por favor) y escribimos http://127.0.0.1

Deberia salir un mensaje de “It works” o algo asi, depende de lo que les pinto en ese momento a los de apache.

Que es “127.0.0.1″ ? es la direccion ip de lookup, es decir, la direccion ip de nuestro “localhost”, nuestra computadora se refencia asi misma con esa direccion ip, en todas las computadoras del universo casi siempre es asi, siempre que hagamos ping a 127.0.0.1 estamos haciendonos un ping a nosotros mismos.

 

Ok, ahora vamos a plantear un escenario comun, imaginemos que queremos trabajar en 2 proyectos diferentes, uno se llama “los simpsons” y el otro “dale gas”. Como hacemos para tener bien separados estos proyectos? Vamos a la solucion.

Primero que nada, entendamos que apache es un servidor web, cada servidor web puede gestionar varias paginas web, cada pagina web tecnicamente es conocida como “virtual host”.

Un virtual host es una declaracion en la configuracion de apache donde basicamente le decimos que si alguien escribe en su navegador “lossimpsons.com” debe leer los archivos del directorio que le indiquemos, como asi tambien si escriben “dalegas.com”.

Los virtuals hots en apache dentro de ubuntu se encuentran en el directorio

/etc/apache2/sites-enabled/

Asi que vamos a crear un virtual host.

sudo gedit /etc/apache2/sites-enabled/lossimpsons

Se nos abre un editor de texto, asi que vamos a escribir este contenido en su interior

<VirtualHost *:80>
ServerName lossimpsons.com
DocumentRoot /var/www/lossimpsons.com/public_html
</VirtualHost>

Guardamos el archivo y cerramos el editor.

Ahora tenemos que reiniciar apache (ya dije como se hace).

Pero claro, lossimpsons.com no estan resolviendo a la direccion de internet que tengamos en ese momento, entonces como podemos hacer para probarlos?

En los sistemas operativos existe un archivo conocido como “archivo hosts” donde se pueden redefinir ciertas reglas a la hora de resolver un dominio a una direccion ip, y con estas reglas podemos forzar que un dominio tenga la direccion ip que nosotros querramos (obviamente esto solo afectara a nuestra computadora).

Para editar nuestro archivo hosts ejecutamos:

sudo gedit /etc/hosts

En mi caso, el archivo hosts de mi computadora se ve asi:

 

192.168.0.109	lortmorris-laptop	# Added by NetworkManager
127.0.0.1	localhost.localdomain	localhost
::1	        lortmorris-laptop	localhost6.localdomain6	localhost6
127.0.1.1	lortmorris-laptop
127.0.0.1       lossimpsons.com

Como veran, en la ultima linea agregue que la ip 127.0.0.1 es para el dominio “lossimpsons.com”.

Esto hara que cuando hagamos ping a lossimpsons.com la direccion ip devuelta sea “127.0.0.1″.

Ponemos en nuestro navegador entonces “lossimpsons.com” y vemos que pasa.

Si nos da error, es simplemente poque no hemos creado el document root de ese virtual host, el document root es el directorio en el cual estara nuestro sitio web (virtual host), por lo que vamos a crearlo.

mkdir /var/www/lossimpsons.com
mkdir /var/www/lossimpsons.com/public_html

Ahora vamos a meter un archivo ahi adentro para saber que todo funciona bien

echo "<?php phpinfo(); ?>"  > /var/www/lossimpsons.com/public_html/index.php

Ahora volvemos a nuestro navegador y vemos que pasa.

Si todo sale bien deberian ver una pagina web con informacion muy “rara” sobre php, apache, etc. Eso quiere decir que todo quedo bien.

Les comento para los que no se dieron cuenta, que en el comando anterior (echo …) generamos un archivo.php donde en su interior hay una funcion llamada php_info.

Lo que deberia ver es algo como esto

 

Bueno, de la misma forma que generamos el primer virtual hosts, podemos generar todos los que queramos.

Cualquier duda o comentario dejarlo en este blog :)

 

Escondiendo el codigo php

June 9th, 2010 No comments

Bueno, he tenido una necesidad que todos en algun momento tenemos, intentar complicar la lectura de nuestro codigo PHP.

En mi caso, arme un script muuuy simple, donde paso por base64 los archivos que me interesan.

$secre=array("a","b","c","d","e","f","g","h","i","j","k");

for($x=0; $x<=5; $x++){
	$body=file_get_contents("/path/to/archivo.php");
	$phra=$secre[rand(0,count($secre)-1)].$secre[rand(0,count($secre)-1)].$secre[rand(0,count($secre)-1)].$secre[rand(0,count($secre)-1)];
	$encode=base64_encode($body."\r\n //".$phra);

	$final="eval(base64_decode('$encode'));";
	file_put_contents("/path/to/archivo.php",$final);
}

Básicamente es tomar el contenido del archivo, hacerle un base64, agregarle un comentario con un rand por que me pareció bonito (hay razones, no vienen al caso) y escribir ese resultante en el mismo archivo. Metemos esos procedimientos en un for (limitado a 5, pero podríamos ponerlo a 10, ojo con el consumo de recursos, no limarla).

Saludos.

Categories: php, programacion Tags: , ,

PHP 5.3, la furia de los programadores

May 10th, 2010 4 comments

PHP 5.3 ha despertado una verdadera indignacion en los programadores de la Web and Beer.

Demian Rodriguez ha iniciado el debate, y se ha puesto muy violento cuando se entero como finalmente funcionaran los namespace.

Aca vamos a citar lo mencionado por Demian/

“Queria probar los namespaces para tratar de hacer algo util, ya que tardaron tanto en implementar esta garcha que tienen que servir para algo… solo voy a poner unos ejemplos que demuestran lo contrario:”

namespace phpsucks;

class A {
function __construct() {
$d = new DirectoryIterator('./'); // Fatal error: Class 'phpsucks\DirectoryIterator' not found
throw new Exception(); // Fatal error: Class 'phpsucks\Exception' not found
}
}

$a = new A();

class SuperException extends Exception {
// Fatal error: Class 'phpsucks\Exception' not found
}

class Conf implements ArrayAccess {
// Fatal error: Interface 'phpsucks\ArrayAccess' not found
}

“O sea que no hay fallback al namespace global para las clases. Si a alguien le interesa organizar la clases con namespaces para hacer un lindo autoload o lo que fuere, se rompe todo y tienen q meter una contrabarra adelante de cada clase. Les parece óptimo?”

“Que futuro le ven a esto? no les parece que este lenguaje esta siendo desarrollado por gente inutil? no les parece ya demasiado bizarra la sintaxis? no les da ganas de programar en otro lenguaje? estoy exagerando?”

A todo esto, Andres dejo sus comentarios al respecto

“La verdad no entendí mucho el problema (ni le presté mucha atención).
Solo voy a decr, no sos el único que tiene problemas con PHP. Algunos
optan por hacer forks con distintas características (sea para
optimizar el procesamiento, como HipHop, o para modificar la sintaxis
como en el caso ese que salió el otro día que tira hacia una onda más
pythónica).
Otros optan por cambiar de lenguaje.

Ponete a programar en Python y no jodas jajaja”

Y para finalizar, la ultima observacion de Demian.

otra villada son los late static bindings, ahora si queres que “te ande bien” cuando una clase extiende de otra, en vez de usar self::method() tenes que usar static::method()… no podian reutilizar el self??
soporte de unicode? y recien para el 2012 con suerte cuando salga php 6…
new php\y\la\concha\de\tu\hermana(); manga de lelos voy a leerme el libro de python de 1000 paginas que tengo aca.

Instalar PHP 5.2.x en Centos

August 4th, 2008 1 comment

Estimado, tube un caso con un cliente al cual tube que instalar el PHP 5.2.x en su Centos 4.x, y fue una cosa que me volvio loco.

Despues de mucho googlear, encontre esta solucion que quiero compartir.

http://www.jasonlitka.com/2007/06/01/upgrading-to-php-523-on-rhel-and-centos/

[Articulo]

PHP 5.2.3 has been released. This version does not have the massive number of bug fixes that came with 5.2.2 (which included many of the patches from the Month of PHP Bugs), but it does have some useful fixes included, including the fix for the HTTP_RAW_POST_DATA bug that was introduced in PHP 5.2.2.

This build was relatively straight forward and didn’t require any modifications to the spec file from my build of 5.2.2. I’ll probably do a respin when MySQL 5.0.42 is finally marked as “released” (the source package is already available but the changelog hasn’t been updated with an official release date).

For those that like to build from source, feel free to use the Source RPM from the link below. For my repository users, the new version is already available and all you’ll need to do is run a “yum update”.

UPDATE (6/19/2007): The PHP 5.2.3 packages have been updated. The Source RPM link below has also been updated to prevent anyone from downloading an out-of-date file.

If you want compile the src rpm yourseld here are the directions.

mkdir /usr/src/redhat
chmod -R 777 /usr/src/redhat

rpm -ivh php-5.2.3-jason.1.src.rpm

cd /usr/src/redhat/SPECS/

here you have a file dot spec with the configure and build lines.

rpmbuild -bb name_of_your_package.spec (in this case i think that is PHP)

when all finish right you have one or many package in the RPMS folder, you need update your current rpms with this, with the following command.

rpm -Uvh name_of_package_output.rpm


PHP 5.3 Alpha 1

August 1st, 2008 1 comment

PHP anunció la disponibilidad del release Alpha 1 de PHP 5.3.

Se trata del primer bosquejo de pruebas de lo que será la versión 5.3 de PHP, que si han seguido más o menos los distintos posts que al respecto se hicieron en distintos espacios de la red, es la que incluirá la mayoría de los nuevos features copados en los que viene trabajando el equipo de desarrollo últimamente. Podríamos decir que es PHP 6 sin el soporte para Unicode.

Entre los cambios más importantes se destacan:

Categories: novedades, php Tags: , , ,

Pasar imagen a Texto (muy divertido)

July 17th, 2008 1 comment

Version JPG

> 16) & 0xFF;
                    $g = ($rgb >> 8) & 0xFF;
                    $b = $rgb & 0xFF;
                    $posy = $y + $fontsize;
                    imagettftext ($im2, $fontsize, 0, $x, $posy, $rgb, $font, $frase[$j]);
                    $j++;
                    if($j == count($frase)){
                        $j = 0;
                    }
                }
            }
            //$blanco = imagecolorallocate($im2, 255, 255, 255);
            //imagettftext ($im2, 20, 0, 0, 20, $blanco, $font, 'Code by Musky');    //Si le queremos poner un copyright
            imagejpeg($im2, "generada3.jpg");
            imagedestroy($im2);
            imagedestroy($im);

      }
      genera_imagen();/*

        Code by Jonathan Ariel Muszkat
            muskys@gmail.com
*/

?>

El modelo de Drupal

July 4th, 2008 No comments

El otro día estaba leyendo un post en un blog de un personaje conocido en el mundo de PHP, criticando a un framework, y me llamo la atención los puntos que marco como deficientes.

Que seria lo correcto o incorrecto ?

La realidad indica que lo funcional es lo correcto, pero es tan asi?

Nosotros sabemos que al mundo lo rigen ciertas reglas, de cosas que deberían ser, y realmente en PHP se hace lo que se quiere sin respetar estas reglas, y es muy difícil destacarse en este ambiente, tan mal visto por verdaderos gurus de la programacion. Al manejar muchos pseudo lenguajes (js, html, css, xml, json, etc), se complica un poco la organizacion, y si además a eso le agregamos que PHP es un hibrido que permite estructurado y POO. Y que hasta estos días no se conoce un estándar de código, mas allá de lo que tenemos en la documentacion de Zend, y que no somos muchos los que lo implementamos. Esto se traduce ha mucho código programado y pensado en muchas variedades diferentes.

Hace unos días empecé a trabajar con Drupal, para el frontend de un portal, y su código podría ser catalogado como incorrecto, pero la funcionalidad y la potencia de este CMS, lo hacen único. Su flexibilidad, su extensionalidad, y lo fácil que resulta lo hacen un gran CMS, sino el mejor. Pero la mayoría de su código son funciones, ni siquiera PHP4 solo funciones, bien estructurado, muchas de esas funciones nos devuelven HTML puro, así es HTML en una función, y generamos las distintas partes de un themes a través de esto.

Y para seguir hablando de lo poco correcto del código, tengo que decirles otra cosa mas (lo que va a leer ahora puede ser nocivo para la salud), usa variables globales!.

Sin embargo Drupal es el CMS mas popular, eficiente, fácil, y extensible del mercado.

Así y todo yo apoyo 100% el uso POO en PHP, creo que PHP5 es uno de los grandes avances que tuvimos, y es la forma en que la gente de Zend nos dice, “Estamos evolucionando, sigan con nosotros”, no solo PHP5, sino los magníficos Frameworks que tenemos hoy en el mercado como symfony y Zend Framework, entre otros, que día a día siguen extendiendo funcionalidades, de la mejor forma posible usando POO.

Posibles soluciones: que en las próximas versiones la gente de Zend, elimine la programacion estructurada de su código, y obliga al mejor ejemplo java a usar solo POO, pero esto tiene mas de una contra, va a perder popularidad, el costo de aprendizaje va a ser mayor. Pero vamos a ser mas respetados como programador, los que sigamos con PHP, vamos a tener un camino definido bajo estándares de código, y siguiendo una linea. Sin embargo no creo que sea lo mejor.

En definitiva, creo que lo mejor es que tomemos el tren que mejor nos quede.

Ustedes que piensan?

Enlaces:
Drupal
Zend Framework
Symfony
Estandar de código

Oracle/PHP for starters: Ponele bindings a esa query!

June 30th, 2008 No comments

¿Qué son los bindings?

Oracle tiene una facilidad de fundamental importancia que a veces suele llamarse variable binding, pero que es más comunmente conocido (también en otros DBMS como MySQL, SQL Server o Postgre) como prepared statements o “sentencias preparadas”, por la forma en que se utiliza esto a nivel código.

Lo explicaré desde el punto de vista de Oracle, que por lo que conozco es bastante más completo (y me es más familiar) y luego comentaré brevemente cómo se puede implementar en otros sistemas como MySQL.

¿A qué llamamos prepared statements?
Un prepared statement es básicamente una consulta SQL que se define en forma genérica con el fin de ser reutilizado varias veces a lo largo de la ejecusión de un mismo programa. Como generalmente una query suele tener filtros por determinados campos, donde comparamos el valor de uno o más campos contra uno o más valores variables, lo que utilizamos al definir el prepared statement es definir variables dentro de la query.

Optimización de consultas

Veamos esto con un ejemplo.

Supongamos que tenemos una consulta para obtener los datos de un usuario a partir de su ID:

$sSql = “SELECT user_name, user_password, user_birthdate FROM user WHERE user_id = ” . $iUserId;

Dejaremos de lado por un momento los problemas de seguridad de esta query para ver qué sucede cuando se ejecuta en Oracle. Al ejecutarse esa query, por ejemplo cuando la variable $iUserId es igual a 5, se enviará a Oracle la siguiente consulta:

SELECT user_name, user_password, user_birthdate FROM user WHERE user_id = 5

Oracle tomará la consulta, elaborará un plan de ejecusión (dependiendo de muchas variables como índices, claves primarias y foráneas, estadísticas específicas de la base de datos, etc.), ejecutará la query y devolverá los resultados. Adicionalmente, esta query será almacenada en una suerte de cache que tiene Oracle con las últimas queries ejecutadas (no recuerdo el número exacto de consultas que se cachean, pero por decir algo digamos que son 100), junto con su plan de ejecusión. De esta manera, cuando una misma query se ejecuta varias veces seguidas, Oracle no debe estar armando el plan de ejecusión cada vez.

Supongamos que, seguidamente, ingresa otro usuario al sistema y se ejecuta la query para obtener los datos del usuario con ID = 6. Se ejecutaría la siguiente consulta:

SELECT user_name, user_password, user_birthdate FROM user WHERE user_id = 6

Oracle repetiría los pasos que describimos recién: plan de ejecusión, ejecusión, devolución de los resultados. ¿Por qué no utiliza el cache del que hablabamos?, porque evidentemente para Oracle las dos queries son diferentes: una tiene un 5 y la otra un 6. El query string, digamos, es diferente por lo tanto Oracle no tiene por qué pensar que el plan de ejecusión va a ser el mismo.

¿Cómo hacemos para que Oracle sepa que la query es la misma? Definimos ese $iUserId que usamos en PHP como una variable que pueda reconocer Oracle. Para ello, de la misma forma que en cualquier lenguaje, deberemos definir un nombre para la variable, ubicarla dentro de la query y luego asignarle un valor a esa variable. Dentro de la consulta SQL, las variables se identifican porque comienzan con dos puntos “:”. La consulta que teníamos quedaría así:

$sSql = “SELECT user_name, user_password, user_birthdate FROM user WHERE user_id = :iUserId”;

Luego, asignamos el valor a la variable en un array.

$aBindings = array (“iUserId” => $iUserId);

Por último, ejecutamos la query con nuestra clase de base de datos:

$aResult = $oDB->sql_query($sSql, $aBindings);

En pruebas en producción, con queries sobre tablas con cientos de miles de registros, creanmé que las diferencias de preformance son notables.

Consultas más seguras

Pero esto tiene otra ventaja adicional, en relación a un problema cotidiano de cualquier desarrollador, y sobre todo de cualquier desarrollador web: el escapado de las variables. Muchos freaks recordarán la famosa viñeta de xkcd al respecto.

El iUserId (o cualquier otra variable que usemos para filtrar), en muchos casos vendrá de un dato introducido por el usuario y debemos asegurarnos de validar esa información correctamente para evitar riesgos de SQL Inyection, es decir que en esa variable se introduzca código SQL que pueda modificar (en algunos casos muy perjudicialmente, HolaCine, cof, cof) el comportamiento esperado de la consulta. Ahora bien, yendo a la lógica más naif: ¿Si $iUserId es una variable que utilizo para filtrar, por qué le voy a meter SQL? Claro, hasta hace un rato Oracle no tenía ni idea que iUserId era una variable porque a él le llegaba el string de SQL entero y el ejecutaba.

Con los bindings Oracle sabe que :iUserId es una variable (de hecho, al momento de bindear con el statement se le puede indicar de qué tipo de dato es esa variable), por lo tanto no hay ninguna razón para evaluarlo como SQL. Nosotros podríamos tener:

$sSql = “SELECT user_name, user_password, user_birthdate FROM user WHERE user_id = :iUserId”;
$aBindings = array (“iUserId” => ‘”" OR 1 = 1; DROP TABLE user;’);
$aResult = $oDB->sql_query($sSql, $aBindings);

Y no vamos a tener ningún problema de inyection. Ojo, en ese caso en particular lo que sí vamos a tener es un error de Oracle diciendo que el valor que le pusimos a iUserId no es del tipo de dato adecuado (a menos que seamos tan nardos de poner un ID en un campo char/varchar).

Además, con los bindings no necesitamos preocuparnos por andar escapando comillas, porque el SQL statement y los valores viajan por canales separados y no como un string concatenado.

Cómo implementar prepared statements

Hasta ahora, a lo largo del artículo, utilicé siempre una ficticia clase de base de datos pero no dije cómo funcionaba internamente. Creo que no tiene sentido que reproduzca aquí la clase que uso, porque no es mía, tiene algunos problemas y seguramente ustedes podrán hacer alguna mejor. Voy a reproducir un pequeño ejemplo de cómo hacer una query con bindings usando la librería Oci8. Luego también está la alternativa con PDO que en todo caso veré para la próxima si armo algún ejemplo.


// Este sería el valor con el que vamos a filtrar
$mValue = ‘some value’;

// El prefetch define la cantidad de registros para la cual la librería alocará
// memoria inicialmente, en cada “ida” a buscar información.
// 200 suele ser un número recomendable cuando la query retornará varios
// registros.
$iPreFetch = 200;

// El array donde guardaremos la respuesta
$aResponse = array();

// Nos conectamos
$rConn = ocilogon($sDbUsername, $sDbPassword, $sDbName);

if ($rConn) {
// Armamos la query
$sSql = “SELECT field_1, field_2 FROM table WHERE field_3 = :value”;

// Definimos el array de bindings
$aBindings = array (
‘value’ => $mValue
);

// Generamos el statement
$oStmt = ociparse($rConn, $sSql);

// Asociamos los bindings
foreach ($aBindings as $sWildcard => $sValue) {
@ocibindbyname($oStmt, “:” . $sWildcard, $sValue, -1);
}

// Vemos si hubo algun error al generar el statement
$err = ocierror($oStmt);
if(!$err) {
// Seteamos el prefetch
ocisetprefetch($oStmt, $iPreFetch);

// Ejecutamos el statement
$bOk = ociexecute($oStmt, OCI_DEFAULT);

// Vemos si hubo algún error en la ejecusión
$err = ocierror($oStmt);

// Si estuvo todo bien armamos el record set
if ($bOk) {
while(ocifetchinto($oStmt, $aRow, OCI_ASSOC+OCI_RETURN_NULLS+OCI

_RETURN_LOBS+OCI_NUM)) {
$aResponse[] = $aRow;
}
print_r($aResponse);
} else {
die(“SQL error”);
}

// Liberamos la memoria del statement
ocifreestatement($oStmt);
}
}
?>


En otros DBMS

Otros DBMS como MySQL o SQL Server también soportan queries con bindings. Actualmente, la mejor forma que he visto es con PDO porque lo maneja de la misma forma que con Oracle. Para MySQL desde PHP también puede hacerse con la clase Mysqli, el problema es que en vez de poner nombres de variables en la query uno debe usar signos de pregunta “?”, lo cual trae varios problemas: si una variable se usa más de una vez en una query se debe enviar repetidas veces; los valores de las variables deben ser enviados en orden; la comprensibilidad de la query en sí resulta más complicada.

Al implementarlo de la misma forma que en Oracle, con PDO se resuelve este problema.