domingo, 22 de agosto de 2010

Calcular la edad a partir de la fecha de nacimiento con MySql

Haciendo uso de algunas de las funciones para el manejo de fechas de MySql, calculamos la edad de un individuo a partir de su fecha de nacimiento.

#formato de fecha yyyy-mm-dd
SELECT
   usuarioID,
   nombre,
   YEAR(FROM_DAYS(DATEDIFF(NOW(), fecha_nacimiento))) as anos,
   MONTH(FROM_DAYS(DATEDIFF(NOW(), fecha_nacimiento))) as meses,
   CONCAT(
      YEAR(FROM_DAYS(DATEDIFF(NOW(), fecha_nacimiento))), ' años, ',
      MONTH(FROM_DAYS(DATEDIFF(NOW(), fecha_nacimiento))), ' meses'
   ) AS EDAD
FROM
   usuarios
sábado, 21 de agosto de 2010

Equivalente a strip_tags en ASP clásico

PHP nos tiene acostumbrados a facilitarnos todo tipo de tareas, incluyendo funciones útiles para cualquier propósito de forma nativa, algo que no ocurre en ASP que te obliga a fabricarte tus propias subrutinas "a mano".

Por ejemplo en ASP clásico no existe un equivalente para strip_tags() que elimina los tags HTML a la cadena que se le pase como parámetro.

Aquí tienes dos funciones que hacen esto utilizando expresiones regulares:

'devuelve la cadena totalmente limpia de tags HTML
Function strip_tags(strHTML)
    Dim regEx
    Set regEx = New RegExp
    With regEx
       .Pattern = "<(.|\n)+?>"
       .IgnoreCase = true
       .Global = true
    End With
    strip_tags = regEx.replace(strHTML, "")
    Set regEx = Nothing
End Function

'Esta segunda funcion permite pasar
'una lista de tags admitidos
Function strip_tags(strHTML, allowedTags)
   Dim objRegExp, strOutput
   Set objRegExp = New regexp
   strOutput = strHTML
   allowedTags = "," & LCase(Replace(allowedTags, " ", "")) & ","

   objRegExp.IgnoreCase = true
   objRegExp.Global = true
   objRegExp.MultiLine = true
   objRegExp.Pattern = "<(.|\n)+?>"
   Set matches = objRegExp.execute(strHTML)
   objRegExp.Pattern = "<(/?)(\w+)[^>]*>"

   For Each match In matches
      tagName = objRegExp.Replace(match.value, "$2")
      If instr(allowedTags, "," & lcase(tagName) & ",") = 0 then
         strOutput = replace(strOutput, match.value, "")
      End If
   Next

   strip_tags = strOutput
   Set objRegExp = Nothing
End Function
miércoles, 18 de agosto de 2010

Evitar inyección SQL con PHP

La inyección SQL es un tipo de vulnerabilidad muy común y relativamente sencilla de explotar (que me lo digan a mi). Se origina por una incorrecta gestión de las variables de entrada empleadas para construir nuestras consultas SQL.

PHP ofrece una función específica para prevenir este inconveniente mysql_real_escape_string() que básicamente se encarga de escapar todos los caracteres especiales de una cadena para su uso en una sentencia SQL.

$val = $_POST["txtId"];
$val = mysql_real_escape_string($val);
$sql = "SELECT * FROM tbl WHERE id = $val";
martes, 17 de agosto de 2010

Recuperar posición puntero después de utilizar un conjunto de resultados en PHP

Esta es una forma práctica de recuperar una posición concreta o resetear complentamente un conjunto de resultados después de que estos ya han sido utilizados.

Por ejemplo, si recorremos el resultado de cualquier consulta con mysql_fetch_assoc() o mysql_fetch_array() cuando queramos volver a acceder a ese conjunto de resultados ya no estará disponible puesto que el puntero interno se situa en la última posición.

Con mysql_data_seek() podemos mover el puntero a la fila específicada para el identificador de resultado. Debemos indicar dos parámetros, el primero corresponde al resultado que proviene de una llamada a mysql_query() y el segundo es la posición o número de la fila deseada del nuevo resultado, la siguiente llamada a mysql_fetch_row() devolverá esa posición.

$conn = mysql_connect("localhost", "user", "pass")
  or die(mysql_error());
mysql_select_db("basedatos", $conn);
$result = mysql_query("SELECT * FROM tabla", $conn)
  or die(mysql_error());
//recuperamos por primera vez el conjunto de resultados
while ($row = mysql_fetch_assoc($result)) {
  foreach ($row as $name => $value) {
    $salida .= $name . ": " . $value;
  }
}
//ahora el puntero interno se encuentra al final
//por lo que si queremos recuperar nuevamente los
//valores de $result debemos mover de nuevo
//a la posición 0
mysql_data_seek($result, 0);
//ahora podemos volver a recorrer el conjunto de resultados
while ($row = mysql_fetch_assoc($result)) {
  print_r(mysql_fetch_assoc($result)); 
}

Instalar Apache, PHP y MySql en Windows

Primero descargamos la última versión del servidor web Apache. Elegimos las opciones por defecto que nos ofrece el asistente de instalación. Una vez instalado accedemos a su configuración localizando el archivo httpd.conf. De momento lo único que comprobaremos es que el servidor Apache esté escuchando un puerto libre (Listen 8080). Lo lógico sería atender las solicitudes del puerto 80 que es el estándar, pero si ya está previamente instalado el servidor web Microsoft IIS (como es mi caso), Apache debe escuchar las peticiones que lleguen por el puerto 8080 para no entrar en conflicto, por lo que en este caso para comprobar que todo está OK escribimos en la barra de dirección del navegador http://localhost:8080

Descargamos la última versión de PHP, como lo vamos a usar con Apache, hacemos caso de las recomendaciones y elegimos la compilación VC6 x86 Thread Safe: "If you are using PHP with Apache 1 or Apache2 from apache.org you need to use the VC6 versions of PHP".

Copiamos el contenido del zip en C:\php

Ahora vamos a indicarle al servidor web Apache que debe utilizar PHP, para ello regresamos al archivo httpd.conf y localizamos la sección "Dynamic Shared Object (DSO) Support" y añadimos la siguiente línea:

LoadModule php5_module "c:/php/php5apache2.dll"

Importante: Si la versión de Apache es 2.2.x o superior la librería que hay que referenciar es php5apache2_2.dll

Apache 2.2.x Support: Users of Apache 2.2.x may use the documentation below except the appropriate DLL file is named php5apache2_2.dll and it only exists as of PHP 5.2.0.

Justo debajo de esta línea añadimos:

AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
PHPIniDir "C:/php"

Localizamos la sección DirectoryIndex y añadimos index.php como página de inicio:

<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>

Reiniciamos el servidor y ya debería funcionar PHP correctamente. Ahora solo queda adaptar el archivo php.ini a nuestras necesidades, habilitando los módulos o extensiones que vayamos a necesitar, no sin antes indicar la ruta donde se encuentran así:

extension_dir = "C:/php/ext/"

Por último, instalamos MySql siguiendo el asistente. La última vez que lo instalé me encontré con que no podía conectar vía mysql_connect() utilizando "localhost" como nombre del servidor ya que lanzaba el siguiente error:

Se produjo un error durante el intento de conexión ya que la parte conectada no respondió adecuadamente tras un periodo de tiempo, o bien se produjo un error en la conexión establecida ya que el host conectado no ha podido responder.

Este inconveniente se debe a la incapacidad del sistema operativo de resolver "localhost" hacia 127.0.0.1 Esto sucede tanto en Windows Vista como en Windows 7 ya que por defecto el archivo Hosts ubicado en C:/Windows/System32/drivers/etc trae la siguiente línea comentada:

#127.0.0.1     localhost

Solo hay que quitar la almohadilla y a funcionar.

lunes, 16 de agosto de 2010

MySql, PHP y la codificación utf8

Para evitar problemas con acentos, eñes, etc. y de paso maximizar la compatibilidad de nuestras páginas y bases de datos MySql con cualquier idioma y distintos conjuntos de caracteres, lo mejor es utilizar o establecer por defecto el tipo de codificación UTF-8.

La mejor opción es configurar el servidor MySql para que trabaje con codificación UTF-8 por defecto añadiendo estas líneas en el archivo my.ini localizar [mysqld].

default-character-set = utf8
character-set-server = utf8
collation-server = utf8_general_ci
init_connect = 'SET collation_connection = utf8_general_ci'
init_connect = 'SET NAMES utf8'

Si te interesa profundizar en el tema de los "encodings" y mysql no te pierdas este artículo del Blog personal de Jynus.

El servidor Apache también dispone de una directiva llamada AddDefaultCharset que debemos establecer como utf-8 para que no aparezcan caracteres extraños en lugar de acentos, eñes, etc.

AddDefaultCharset utf-8

En el php.ini la directiva default_charset también tiene algo que ver en todo este galimatias de tipos de codificación y caracteres.

default_charset = "iso-8859-1"

Mediante PHP también es posible configurar el charset en el momento de usar cualquier tabla, para ello debemos especificar el siguiente código después de realizar la conexión y la selección de la BD (mysql_connect – mysql_select_db) y antes de ejecutar cualquier consulta.

mysql_query ("SET NAMES 'utf8'");

En PHP 5.2 o superior podemos también usar:

mysql_set_charset("utf8", $conn);

Una función muy práctica que sirve para ver el charset que está devolviendo MySql.

mysql_client_encoding($conn);
sábado, 14 de agosto de 2010

Leer archivo XML con PHP utilizando simpleXML

Uno de los parsers XML más sencillos de utilizar que nos ofrece PHP es simpleXML. Viene por defecto en la versión 5 y posteriores de PHP. SimplePHP añade una serie de métodos para acceder de forma bastente sencilla a nuestros archivos XML.

//dado el siguiente archivo XML
<?xml version="1.0" encoding="utf-8"?>
<items>
  <item id="1">
    <nombre>Producto Uno</nombre>
    <precio>250</precio>
    <cantidad>2</cantidad>
  </item>
  <item id="2">
    <nombre>Producto Dos</nombre>
    <precio>120</precio>
    <cantidad>1</cantidad>
  </item>
  <item id="3">
    <nombre>Producto Tres</nombre>
    <precio>20</precio>
    <cantidad>5</cantidad>
  </item>
</items>

//cargamos el archivo XML
$xml = simplexml_load_file("items.xml");
//mostramos todo el contenido del archivo
$str = $xml->asXML();
//extraemos directamente un elemento
$producto = $xml->item[0]->nombre;
//obtenemos un array con todos los nodos
//que dependen del principal
$arr = $xml->children();
//recorremos la matriz de items buscando
//a la vez los descendientes del primer item
foreach ($arr->children() as $etiqueta => $valor) {
  echo $etiqueta . ": " . $valor;
}
//acceder a los atributos
//mostramos el atributo id del segundo item
echo $xml->item[1]["id"];
//mostramos solo los elementos con el atributo id par
foreach ($arr as $item) {
  if ($item["id"] % 2 == 0) echo $item->nombre;
}

Redirección 301 con mod_rewrite

Como ya hemos visto en apuntes anteriores podemos hacer una redirección 301 con ASP clásico o esa mismo tipo de redirección con PHP. Ahora vamos a ver como hariamos la redirección "301 Moved Permanently" utilizando mod_rewrite.

RewriteEngine On
RewriteCond %{http_host} ^dominio.com
RewriteRule ^(.*) http://www.dominio.com/$1 [R=301,L]

En este caso lo que hacemos es redirigir todas las peticiones que llegen sin www hacia su equivalente con www. de esta forma evitamos la duplicidad de url's.

Redirección 301 con PHP

Lo dicho cuando hablamos de la redirección 301 con ASP clásico es aplicable en este caso. La forma de hacer una redirección en PHP es igual de sencilla.

Header( "HTTP/1.1 301 Moved Permanently" );
Header( "Location: http://www.dominio.com" );

Header se utiliza para enviar una cabecera HTTP al navegador o cliente y debe ser llamada antes de que sea enviado cualquier otro tipo de salida, atención a los espacios o líneas en blanco que puedan colarse.

miércoles, 11 de agosto de 2010

Mostrar archivos dentro de un directorio con PHP

Resulta sorprendente la facilidad de uso de este lenguaje de programación, algo que vuelve a quedar patente en el siguiente ejemplo en el que con muy pocas líneas de código recorremos una carpeta o directorio y extraemos los nombres de todos los archivos con los que nos encontramos.

$d = opendir("./directorio");
while (($archivo = readdir($d)) !== false) {
  //readdir devolvera FALSE cuando
  //ya no encuentre ningun archivo
  $archivos[] = archivo;  
}
closedir($d);
//ahora tenemos un array con el nombre de
//cada uno de los archivos del directorio indicado

//si queremos limpiar el resultado eliminando "." y ".."
if ($archivo != "." && $archivo != "..") {
  $archivos[] = archivo;
}

Obtener rutas o paths con PHP

Obtenemos las distintas partes de una ruta con php, mediante el uso de pathinfo() accedemos a la información relativa a una ruta de archivo. Podemos indicar qué elementos son devueltos con el parámetro opcional options las distintas opciones son:

PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION y PATHINFO_FILENAME.

Si no indicamos ninguna de estas opciones pathinfo() devolverá una matriz con todos los elementos.

//realpath() devuelve el nombre de la ruta absoluta canonizada
$realPath = realpath("archivo.txt");
$pathParts = pathinfo($realPath);
//accedemos a las distintas partes de la ruta
$nombreDirectorio = $pathParts["dirname"];
$nombreArchivo = $pathParts["basename"];
$extension = $pathParts["extension"];

Pero si lo que buscamos es acceder a la base del arbol de documentos del servidor, podemos hacerlo de la manera tradicional.

$_SERVER['DOCUMENT_ROOT'];

Eliminar acentos y otros caracteres con ASP clásico

Una práctica función en asp clásico (vbscript) que sustituye vocales acentuadas y otros caracteres por el equivalente que se elija.

Function EliminarAcentos(texto)

    Dim i, s1, s2
    s1 = "ÁÀÉÈÍÏÓÒÚÜáàèéíïóòúüñç"
    s2 = "AAEEIIOOUUaaeeiioouunc"
    If Len(texto) <> 0 Then
        For i = 1 To Len(s1)
            texto = Replace(texto, Mid(s1,i,1), Mid(s2,i,1))
        Next
    End If
    
    EliminarAcentos = texto

End Function

Esta otra función es bastante más completa, utiliza además expresiones regulares para eliminar cualquier caracter no permitido en una URL, lo que nos permite limpiar o formatear una cadena de texto para su uso como URL amigable hacia una página concreta de nuestro sitio web.

Function LimpiarUrl(ByVal texto)
    
    Dim objRegExp
    Set objRegExp = New Regexp
    
    objRegExp.IgnoreCase = True
    objRegExp.Global = True
    
    objRegExp.Pattern = "\s+"
    texto = objRegExp.Replace(texto, " ")
    
    objRegExp.Pattern = "[(?*"",\\<>&#~%{}+.@:\/!;']+"
    texto = objRegExp.Replace(texto, "")
    
    Dim i, s1, s2
    s1 = "ÁÀÉÈÍÏÓÒÚÜáàèéíïóòúüñç "
    s2 = "AAEEIIOOUUaaeeiioouunc-"
    If Len(texto) <> 0 Then
        For i = 1 To Len(s1)
            texto = Replace(texto, Mid(s1,i,1), Mid(s2,i,1))
        Next
    End If

    LimpiarUrl = LCase(texto)

End Function

Leer y escribir un archivo de texto línea a línea con PHP

Un ejemplo de como leer un archivo de texto txt línea por línea con php.

//Abrimos el txt generando un puntero hacia el archivo físico
$fp = fopen("archivo.txt");
//recorremos las diferentes lineas de texto
while (!feof) {
  $line = fgets($fp);
  echo $line . "<br />";
}
fclose($fp);
//importante cerrar el archivo al finalizar

También tenemos a nuestra disposición algunos métodos más para realizar este misma tarea, aunque con cierta funcionalidad extra como son fgetss() y fgetcsv().

//fgetss() elimina todos los tags HTML y PHP que encuentre. 
//Si queremos conservar alguno debemos indicarlo en el
//tercer parámetro, como en el siguiente ejemplo
@$fp = fopen($_SERVER['DOCUMENT_ROOT'].'/ruta/archivo.txt', 'rb');
if ($fp) {
 while (!feof($fp)) {
  echo fgetss($fp, 2096, '<strong><br>').'<br />'; 
 }
 fclose($fp);
} else {
 echo 'Error'; 
}

//fgetcsv() se utiliza para dividir las líneas
//según el caracter de delimitación pasado a la función
//imaginemos un archivo con el siguiente texto:
1,nombre cliente 1,codigo cliente 1,estado cliente 1
2,nombre cliente 2,codigo cliente 2,estado cliente 2
//podemos obtener esta información CSV de la siguiente forma:
$l = 1;
if ($fp = fopen($_SERVER['DOCUMENT_ROOT'].'/ruta/archivo.txt', 'rb')) {
 while ($d = fgetcsv($fp, 2096, ",")) {
  $n = count($d); 
  echo "$n campos en la fila $l<br />";
  $l++;
  for ($i = 0; $i < $n; $i++) {
   echo $d[$i].'<br />'; 
  }
  echo '<br /><br />';
 }
 fclose($fp);

} else {
 echo 'Error'; 
}

Para escribir en un archivo de texto prestamos atención a los modificadores de acceso a ficheros.

"r" => Sólo lectura
"w" => Escritura
"a" => Adición
"r+" "w+" => Lectura y escritura
"b" => Binario (se usa en combinación con alguno de los anteriores, se recomienda para maximizar la portabilidad entre sistemas Linux y Windows.

//Escribir en un archivo una nueva línea
$fp = fopen("archivo.txt", "a");
fputs($fp, "\nTexto a escribir");
fclose($fp);

En realidad fputs() es un alias de fwrite(). Otra opción para leer un archivo con php, es mediante el comando file() con el que no se requiere ningún tipo de puntero hacia el fichero, simplemente abre un archivo y devuelve una matriz con cada línea ocupando una posición. El problema con esta opción llega a la hora de manejar archivos muy grandes ya que pueden generar problemas de memoria. En estos casos siempre es mejor hacerlo accediendo al contenido del fichero línea a línea con fgets() como hemos visto antes.

$salida = "";
$archivo = file("archivo.txt");
foreach ($archivo as $linea) {
  $lineaActual = $linea;
  $salida .= rtrim($lineaActual) . "<br />\n";
}
echo $salida;

Redirección 301 con ASP clásico

La mejor forma de realizar una redirección si se cambia de ubicación una página es a través de la llamada redirección permanente 301. El siguiente código ASP realiza una redirección del tipo "movido permanentemente". Esta es además, la mejor opción para mantener "ordenado" nuestro sitio web de cara a los buscadores.

Response.Status="301 Moved Permanently"
Response.AddHeader "Location", " http://www.dominionuevo.com"
La forma de hacer una redirección 301 con PHP difiere, como es natural, un poco.
martes, 10 de agosto de 2010

Recoger y filtrar campos de formulario con PHP

Bueno, decir que no me gusta demasiado la plataforma de blogs de google, una administración muy pobre, muchas cosas no funcionan como deberian o al menos como a mi me gustaria, realizar cualquier ajuste o corregir alguna cosa se convierte en un calvario y muy poca, por no decir nula documentación oficial en español. Pero ya que lo que quiero es simplemente un lugar donde tener disponible algunos recursos que pueden venir bien en cualquier circunstancia y lugar, voy a utilizarlo como bloc de notas online a ver que tal.

Por cierto, aprovecho para dar las gracias a vagabundia un blog muy recomendable, que me ha sido de gran ayuda para darle un poquito de forma a esto.

Ahora a lo que iba, ejemplo para filtrar entradas desde un formulario en php. Con filter_input tomamos una variable externa concreta por nombre y opcionalmente se puede filtrar.

filter_has_var comprueba que una variable de un tipo concreto existe.

Estas dos funciones admiten INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER o INPUT_ENV

//recoge un campo de formulario vía POST
filter_input(INPUT_POST, "txtform");
//comprueba si viene el campo de formulario txtform
//aunque este vacio devuelve TRUE
filter_has_var(INPUT_POST, "txtform");