martes, 30 de octubre de 2007

Mejorar paginación

Recientemente descubrí un método para poder mejorar el rendimiento de una paginación en MySQL. Generalmente se necesitan dos consultas a la base de datos, una para traer los datos y otra para saber que cantidad de datos hay en total. Lo nuevo, si bien no altera la cantidad de consultas, es que podemos hacer que el servidor MySQL sólo tenga que procesar una vez.

¿Cómo funciona esto?
En el primer pedido, además de pedirle los datos, le decimos al servidor que calcule la cantidad de filas totales de la consulta, y después hacemos una consulta para conseguir ese dato.
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
-> WHERE id > 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();

Esto es especialmente útil si utilizamos una cláusula ORDER BY, o una consulta compleja, con subqueries, múltiples JOIN o muchos cálculos.

Consultar el manual de MySQL

jueves, 25 de octubre de 2007

Autocargar clases

Desde PHP 5, no es necesario cargar todas las clases al principio de cada script, sino que si existe una función __autoload es llamada cuando se quieren utilizar una clase que no existe. Esta función recibe el nombre de la la clase que se quiere cargar y tiene que encargarse de hacer que exista esa clase para evitar que se arroje un error porque no exista.

Este es un buen avance de PHP 5, que desde la versión 5.1.2 también nos brinda la posibilidad de registrar múltiples funciones para hacer esto, en vez de limitarse sólo a una como era anteriormente, a través de spl_autoload_register (la documentación sólo está disponible en inglés).

jueves, 18 de octubre de 2007

Switch con expresiones

Los switch generalmente son muy útiles, pero al mismo tiempo muy limitados. Buscando la forma, podemos extender su uso, para poder evaluar expresiones, y llamar a funciones. Lo que tenemos que hacer es poner las expresiones en los case para que sea el argumento a comparar.

Por ejemplo, podemos hacer lo siguiente



switch (true) {
case ($variable >= 0 && $variable < 3):
echo '$variable está entre 0 y 3';
break;

case ($variable >= 3 && $variable < 7):
echo '$variable está entre 3 y 7';
break;

case ($variable >= 7):
echo '$variable es mayor a 7';
break;
}

De esta forma, en vez de hacer tres IFs encadenados podemos recurrir a esta opción, para que nuestro código sea más legible.

martes, 16 de octubre de 2007

Comillas simples y dobles

Para la mayoría, las comillas simples y dobles, en PHP, son iguales. Sin embargo presentan diferencias fundamentales. Usando comillas dobles existen más caracteres especiales: \n \r \t \\ \$ \" \[0-7]{1,3} \x[0-9A-Fa-f]{1,2} mientras que con comillas simples sólo tenemos \\ y \'.
Sabiendo ya esto, podemos suponer que las comillas simples son más rápidas. Por otro lado, debemos agregar que dentro las comillas dobles se pueden utilizar variables sin concatenar, sino escribiéndolas directamente en su contenido.
Más información pueden encontrar en el manual de PHP, en el apartado de Cadenas.

Por último, vamos a comparar los tiempos de cada uno, en varias circunstancias.
Ver ejemplo

jueves, 11 de octubre de 2007

Manejar fechas

Es normal tener que modificar el formato de una fecha para adaptarlo a un tipo de base de datos, o para presentarlo a un usuario. En general, strtotime nos provee una buena herramienta para hacerlo, ya que soporta el formato tradicional de MySQL y muchos otros, pero el problema suele estar cuando queremos recibir una fecha en formato dd/mm/yy, ya que por defecto esta función toma el formato estadounidense mm/dd/yy y puede confundirse 01/07 con el 7 de enero, en vez del 1 de julio que esperábamos. La solución que ofrece PHP es con la función strptime, que nos permite indicar el formato de la fecha. Sin embargo, esta función es muy nueva (desde PHP 5.1.0) y no está implementada en Windows, con lo que es bastante limitada. Por lo tanto, podemos crear una pequeña función, con este propósito.

function strtotime2($date)
{
if (preg_match('#^((0?[1-9])|([1-2]?[0-9])|(3[0-1]))/(0?[1-9]|(1[0-2]))/((19|20)?([0-9]{2}))$#', $date))
{
list($day, $month, $year) = explode('/', $date);
return mktime(0, 0, 0, $month, $day, $year);
} else
{
return strtotime($date);
}
}

jueves, 4 de octubre de 2007

Parametros flexibles, retornos duros

Al programar una función, o un método de una clase, siempre es lo mejor poder recibir cualquier tipo de parámetro. Ver si es más cómodo mezclar dos parámetros en uno, usando un array, o si un dato puede ser de varios tipos. Todas estas cosas hay que tenerlas en cuenta para que luego sea menos difícil trabajar con lo que ya está hecho. Otra cosa que debemos intentar es siempre devolver el mismo tipo de dato, incluso si no se pudo realizar lo que se esperaba, entonces si se debe devolver un array, pero hubo un error podemos devolver un array vacío en vez de false o null. Un ejemplo simple de esto



function select($nombre, $valores = array(), $seleccionado = '', $atributos = array())
{
if (!is_array($valores) || count($valores) == 0)
{
return '';
}

if (is_string($nombre))
{
if (is_array($atributos))
{
$atributos['name'] = $nombre;
} else
{
$atributos .= ' name="' . htmlentities($nombre) . '"';
}
}
$out = '<select';
if (is_array($atributos))
{
foreach ($atributos as $atributo_nombre => $atributo_valor)
{
$out .= ' ' . $atributo_nombre . '="' . htmlentities($atributo_valor) . '"';
}
} else
{
$out .= ' ' . $atributos;
}
$out .= '>';
foreach ((array)$valores as $valor_clave => $valor_etiqueta)
{
$out .= '<option value="' . htmlentities($valor_clave) . '"' . ($valor_clave == $seleccionado || (is_array($seleccionado) && in_array($valor_clave,$seleccionado)) ? 'selected="selected"' : '') . '>' . htmlentities($valor_etiqueta) . '</option>';
}
return $out . '</select>';
}

echo select('select',array('a' => 'A', 'b' => 'B','c' => 'C','d' => 'D'),array('a'),array('id' => 'select', 'multiple' => 'multiple'));
echo PHP_EOL;
echo select('select',array('a' => 'A', 'b' => 'B','c' => 'C','d' => 'D'),'a','id="select"');
echo PHP_EOL;
echo select('select');