Esta semana me he topado con otro “bug” realmente extraño, esta vez en PHP. Estábamos desarrollando el proyecto en nuestro sitio de desarrollo y el código en cuestión funcionaba bien. Se trataba de una sencilla llamada a un fichero PHP que realizaba una redirección en función del resultado de una consulta.
El cliente decidió cambiar de alojamiento web, así que cogimos todos los “bártulos” (ficheros, base de datos y demás) y nos pasamos al nuevo servidor. Nos encontramos varios problemillas, por ejemplo, que el primer servidor era Windows y el segundo Linux, o que la versión de PHP no era exactamente la misma. Tras varios ajustes de configuración, el sitio salió andando… salvo la sección donde se hacía la redirección. El error que daba era:
Warning: Cannot modify header information – headers already sent by (output started at /todo/el/camino/a/un/include/archivo.php:1) in /todo/el/camino/a/mi/archivo.php on line 37
La línea que daba el error (la 37) era donde estaba la llamada a la función header, responsable de la redirección. Como se explica en la documentación, la llamada a la función debe hacerse antes de enviar hacia el navegador cualquier cosa. El error que estaba apareciendo me notificaba que ya existía una cabecera que había empezado en otro archivo, para más señas en la línea 1.
El fichero donde supuestamente se había enviado algo al navegador era un fichero insertado en el código con una instrucción include. La línea 1 era el clásico <?php donde, claramente, no se envía nada hacia el navegador. En todos los foros donde miré hablaban de espacios, tabuladores u otros caracteres antes de la etiqueta de comienzo de PHP, pero yo no tenía nada (o eso creía yo en aquellos momentos). Además, ¡el mismo código funcionaba en mi servidor de desarrollo Y en el primer servidor de producción!.
Solventamos el problema temporalmente eliminando el include y copiando las funciones necesarias directamente en el mismo fichero. La redirección se hacía correctamente, pero ahora el problema surgía con una llamada a session_start(). La sesión daba problemas porque la cabecera ya había comenzado (otra vez en la línea 1, esta vez del mismo fichero) y la llamada a la sesión debe hacerse en la primera línea de código. Desgraciadamente para mí, no me dio por releer la documentación de session_start() (ya sabía que la llamada se debía hacer en la primera línea, y así lo estaba haciendo), porque la solución estaba en una de las notas de los colaboradores.
Finalmente, y tras varias horas de investigación, descubrí que el problema no estaba en el código PHP sino en la codificación del fichero. En nuestro caso, estábamos usando UTF-8, con el pequeño detalle de que en esos dos ficheros, el Byte-order Mark (BOM para los amigos) estaba activo. El BOM (si está activo, ya que es opcional) es el primer byte del fichero y sirve para indicar si los datos se almacenan como Little Endians o Big Endians.
El detalle que me despistó totalmente (haciéndome pensar que era un problema de configuración de servidor o similar) fue que el mismo código funcionara en dos servidores, pero no en el tercero. Afortunadamente, una muesca más en la lista de “bugs” eliminados.