miércoles, 3 de julio de 2013

Redireccionamiento en PHP

En PHP existe una función llamada header() que se encarga de especificar una cabecera HTTP.
Dicha función debe de ser llamada antes de mostrar nada por pantalla. De otra manera se producirá un error.
Cada vez que enviamos una salida desde el servidor al cliente (o viceversa) dicha información será enviada junto a un conjunto de cabeceras HTTP. PHP enviará cabeceras aunque no las especifiquemos. Estas cabeceras serán por defecto si no se han sobreecrito con header(). Por lo tanto si existe alguna salida anterior a la llamada de header(), ya se habrá procesado el envió de cabeceras. Y se producirá el error: "headers already sent".

 void header ( string $string [, bool $replace = true [, int $http_response_code ]] )  

- El primer parámetro es la cabecera que indicará la acción a realizar. Las tres cabeceras más habituales, en header() son:
  • "Location ...". Es el uso más típico de header(). Con  este encabezado redirigimos a la dirección especificada y enviará un código 302 (redirect) de estado. Por lo tanto enviará las cabeceras especificas hasta el momento y procederá a realizar una redirección. Podemos introducir una ruta relativa al servidor o una absoluta. 
 header("Content-Type: application/json;charset=utf-8");    
 header("Location: info.php");  
  • "HTTP/...". Especificaremos una cabecera que especificará un código de respuesta HTTP de la respuesta al navegador. 
 header("HTTP/1.1 200 OK");   
  • "Content-Type ..." Con esta cabecera especificaremos el formato de la salida que mostrará el navegador del cliente.
  header("Content-Type: application/json;charset=utf-8");   

Evidentemente existen muchos otros tipos de cabecera que podríamos especificar, como "user-agent". Simplemente las tres presentadas son las más usadas. A continuación podemos ver otras cabeceras.

 header("Content-Disposition: attachment; filename=\"$filename\"");  
 header("Expires: 0");  
 header("Cache-Control: must-revalidate, post-check=0,pre-check=0");  
 header("Pragma: public");  

- Opcional. El segundo parámetro indica la posibilidad de que si hay más de una cabecera seguidas del mismo tipo, la última remplace a las anteriores. Por ejemplo si tenemos dos redirecciones, por defecto viene el parámetro a true, se efectuará la segunda de ellas.

 <?php  
 header("location: a.html");  
 header("location: b.html");   
 ?>  

- Opcional. Con el tercer parámetro podemos especificar el código de respuesta HTTP. Aunque este código también lo podemos especificar en la cadena del primer parámetro. Como ya hemos visto.

Por lo que es muy útil para que tras una acción,como podría ser una identificación, el script PHP pueda redireccionar al usuario a otro lugar de la aplicación o externo, sin que este tenga que hacer nada.

El código tras la especificación de las cabeceras será siempre ejecutado. Si queremos que tras enviar la cabecera el resto del código del script no se ejecute, pondremos un punto de salida mediante la función exit(). En las redirecciones también es aconsejable ponerlo, aunque el código tras el header() no se vaya a ejecutar. Es una forma de asegurarse.

Buffer de salida

Si en la configuración de PHP, la directiva output_buffering no tiene el valor "off" (por ejemplo 4096) significa que tenemos activado el buffer de salida. Esto lo podemos cambiar en  el php.ini, que está en /etc/php5/apache2/php.ini o en un fichero .htaccess en la raíz del proyecto. En este último fichero pondríamos lo siguiente:

 php_flag "output_buffering" off;  

El tener activado el buffer de salida implica que podemos enviar cabeceras y cookies incluso después de que  haya una salida. Pero hay que tener cuidado con esto ya que podemos tenerlo activado en nuestro servidor local;  de hecho en una instalación normal es muy posible que venga activado por defecto. Pero en el servidor de producción puede estar desactivado lo que nos mostraría errores que antes no teníamos.

Lo más conveniente sería tenerlo desactivado y trabajar con las funciones especificas de PHP para tratar el buffer de salida. Para ello utilizaremos las funciones ob_start() y ob_end_flush(). Con la primera de ellas activaremos el buffer de salida y almacenaremos las salidas hasta que se indique con la segunda función. Pero solo las salidas, ya que el envío de cabeceras y cookies se puede seguir realizando. Por lo tanto los echo's quedaran almacenados en buffer y no enviarán nada. Y quedará el camino libre para la ejecución de header() sin peligro a que antes de estas llamadas se hayan enviado o no cabeceras a través de salidas.

 ob_start();  
 header("location: a.html");  
 echo "asdfghjkl";  
 header("location: b.html"); //remplaza a a.html y se realiza esta redirección
 ob_end_flush(); 

En este ejemplo como el buffer descarta el envió de cabeceras por medio de echo, almacenándolo en el buffer, se produce el remplazo de las redirecciones.

Ejemplos de uso

- Redirección normal.Tras la siguiente redirección ignoraremos el resto del script.

 header("Location: localhost/home.php");  
 exit();  

- Cabecera con envío de código de respuesta. Como no especificamos una redirección, las cabeceras serán enviada cuando el script produzca una salida.

 header("HTTP/1.1 200 OK");  
 header("Content-Type: application/json;charset=utf-8");  
 ...  
 echo "[{"id":"1","nombre":"prueba","email":"prueba1@ejemplo.com"}]" . "<br/>";  
 echo "Parece que ha ido bien";  
 exit();  

Ejemplo de cabecera de respuesta a una petición HTTP


Errores comunes

Como se comentó antes, header() debe ser llamado antes de mostrar nada por pantalla. Esto incluye  cualquier tipo de salida desde el script antes del header(). Ya sean etiquetas HTML o lineas en blanco.

- Etiqueta HTML y echo(). Error ya que tenemos dos salidas antes de la redirección: etiqueta html y salida mediante echo(). Mostrará un error diciendo que las cabeceras ya fueron enviadas ("headers already sent"). Ya que se ha procesado una salida anterior. Y esto ya enviará cabeceras.

 <html>  
 <?php  
 echo "segunda salida!!!";  
 header("Location: localhost/prueba");  
 exit();  
 ?>  

- Hay que tener en cuenta el comportamiento de las redirecciones. Hemos dicho que tras llamar a header() se ejecuta el código que haya a continuación. Pero evidentemente si el header() provoca una redirección el script deja de ejecutarse (aunque podemos añadir exit() para asegurarnos). Ya que pasará a ejecutarse el script redireccionado. O simplemente se mostrará una página si la redirección es a una página estática. Sin embargo también hemos dicho que por defecto se produce un remplazo de cabeceras seguidas del mismo tipo. Lo que ocurre es que al encontrar una salida, anterior a la segunda redirección, se produce un error (cabeceras ya enviadas). Pero se ejecuta la primera redirección y no se muestra dicho error (PHP ejecuta hasta que encuentra un error).

 <?php  
 header("location: ./contacto.php");  
 echo "redirección";  
 header("location: ./info.php"); 
 ?>  

- Cuidado al incluir un script mediante require o include antes de una llamada a header(). Ya que si dicho script incluido tiene una salida, se generará nuevamente un error. Ya que como anteriormente, ya se habrá procesado un envío de cabeceras antes de header().

Comprobar si cabeceras fueron enviadas

Existe una función en PHP para  asegurarnos que las cabeceras no fueron enviadas y por lo tanto podamos hacer, por ejemplo, una redirección sin peligro a errores. La función headers_sent() devuelve si ha habido algún envió de cabeceras HTTP con true o false.

 if (!headers_sent()) {  
   header('Location: http://www.example.com/');  
   exit;  
 }  

1 comentario:

  1. Excelente nunca habia visto este tipo de redireccionamiento con PHP, anteriormente lo habia hecho de otra forma, pero me gusta, creo que para Google no funcionaria tan perfecto, prefiero hacerlo desde htaccess sin duda.
    Actualmente estamos trabajando con un Hosting con PHP 8.

    saludos.

    ResponderEliminar