Bug en PHP Spl seek SplFileObject. (Solucionado)

Bug Spl Seek PHP < PHP8.0.1


Existe un bug en la función seek() de SPL ( Standard PHP Library ) en PHP < PHP8.0.1. Aquí la solución.

El bug concretamente aparece cuando hacemos un seek() del objecto SPL a la 2ª fila (índice 1). Nos devuelve la 3ª fila en lugar de la 2ª fila, y así con los posteriores índices de seek(). El bug está presente en todas las versiones anteriores a la PHP8.0.1.


Los códigos de los programas que utilizamos a diario los creamos desarrolladores como yo. Muchas veces fallan a causa del uso en algún software o hardware específico. Otras veces fallan por una mala codificación. En definitiva, el hardware ha sido creado por humanos, el código lo teclean personas humanas, y como humanos que somos, podemos cometer errores también, ya sea en el hardware o en el software.

La ventaja del software libre es que el código es público. Lo puedes descargar, modificar, reutilizar, etc... y por otra parte tiene la ventaja que todos los programadores se comunican entre ellos para detectar fallos, mejorar procesos en rendimiento, etc...

Estoy desarrollando una librería en PHP para el manejo de los datasets de archivos en formato .csv que se utilizan para el aprendizaje profundo, que partió de la librería base CSV_PAIR_FILE (o en github.com). En el desarrollo de la librería tuve que utilizar una función en PHP ( Standard PHP Library SPL seek() ) para situar la lectura de una línea de un fichero en una posición específica del mismo, pero me ofrecía el resultado de la línea siguiente, no de la fila esperada. Después de investigar horas y de leer en múltiples ocasiones la documentación oficial de PHP, encontré por fin que se traba de lo que se llama un BUG🐞. Existe una página en la web oficial de PHP donde reportar estos bugs.

Lo que detecté no fue un fallo en mi programa, ya que se utilizaba según la documentación oficial, si no un fallo interno del lenguaje de programación, que es diferente. Para que lo podáis entender, es como si te dieran una herramienta que cuando la utilizas para una tarea en concreto, esa herramienta que se supondría que debería de poder realizar la tarea, te encuentras que no sirve, que falla, que un boli no escribe en un papel determinado pero que en las últimas unidades vendidas si que escribe, que unos zapatos se desabrochan al subir un bordillo en concreto pero que en las últimas unidades vendidas no se desabrocha,.... suena raro, pero en la informática es una comparativa fácil de entender porque funciona así. Es una de las tareas de los programadores. Buscar y encontrar soluciones a estos problemas, ya que imaginad que poniendo un papel debajo del otro, resulta que el boli que no escribía, de pronto vuelve a escribir. Le quitamos el papel de debajo, y deja de escribir. Solución? Poner un papel debajo. Pero el trabajo de los programadores es preparar el sistema para que pueda trabajar con un papel debajo o no, porque nosotros no sabemos qué versión del bolígrafo utilizará el usuario, si una antigua o con una versión del bolígrafo nueva. Hay que escribir el código para las dos versiones y que no falle en caso de utilizar un bolígrafo antiguo.

En datos técnicos:

El fichero empieza en la posición 0, por lo que para situarnos en la fila 0, podríamos hacer:

$Spl->seek(0);

Lógicamente para posicionarnos en la fila 1, tendríamos que hacer:

$Spl->seek(1);

Y aquí es donde está el error, ya que el sistema no se posiciona en la fila 1, sino que se posiciona en la fila 2 (2 de índice. Físicamente se habría posicionado en la línea 3ª). Aquí es cuando empieza la locura y la investigación, para ver si existe algún error que haya pasado por alto en el código tecleado, revisar decenas de veces la documentación oficial, volver a revisar el código tecleado, realizar varios tests (todos ellos erróneos), ver que el código está bien escrito y empezar a investigar por google.... en definitiva, volverse medio loco. Este problema hace que deje de existir la fila 1, imposibilitándonos poder situarnos de ninguna manera en la fila 1 para leer su contenido. El cursor del fichero se posiciona siempre en la siguiente fila de la que nosotros le hemos dicho. La fila 1 simplemente es 'imposicionable', evitando que se pueda acceder a ella de ninguna manera con la función que nos da el lenguaje de programación, que se supone que debería de funcionar correctamente.

El problema viene arrastrado desde las versiones antiguas de PHP hasta la última PHP8.0.0. Parece ser que en esta última, en una revisión está corregido, a partir de la versión PHP8.0.1.

Entonces, ¿qué pasa si tenemos una versión antigua? Pues que existen maneras de solucionar el error en esas versiones, programando código extra que subsane utilizando alguna vía alternativa el error que genera el sistema.

En este caso no he ofrecido la solución reprogramando el código original del sistema PHP, pero la solución que yo implementé fue una función que he publicado en el manual de uso de la página oficial del lenguaje de programación PHP.

Así que al final: Bug en PHP Spl seek SplFileObject. (Solucionado)

Se agradece encontrar instrucciones de uso, saber que existen problemas y no tener que perder el tiempo buscando el por qué no funciona un código que en teoría tendría que funcionar a la perfección, etc... Es por ello que decidí publicar la solución en el manual oficial, porque no estaba documentada y otros programadores se podrán beneficiar de estas soluciones y ahorrarse las horas que yo tuve que invertir para saber qué era lo que pasaba.

En el software libre el esfuerzo de todos se transforma en colaboración para aprender y mejorar, y como no, también se transforma en tiempo.

La solución que desarrollé está publicada en el Manual oficial de PHP:

https://www.php.net/manual/en/splfileobject.seek.php


Y el código fuente en PHP < PHP8.0.1 para solucionar el problema lo tenéis a continuación. Funciona en todas las versiones de PHP, tanto en las antiguas como en las nuevas (vaya, .... en principio😅):


there is a bug using fseek on previous versions of PHP8.0.1
See:
https://bugs.php.net/bug.php?id=46569
https://3v4l.org/O89dJ

I solved it with a function:

<?php
/**
* SEEK an Spl object
* There is a bug in php for seeking files
* seems solved php_version >= PHP8.0.1
* See https://bugs.php.net/bug.php?id=46569
* & https://3v4l.org/O89dJ
*
* $Spl->seek() Works ok in all versions with offset 0 (first row)
* On PHP_VERSION < 8.0.1:
*  - Offset 1: seek() cannot seek at row 1. It will be done manually, rewind file and reading first row
*  - Rest of Offsets: The cursor remains at next row of $Offset
*
* @param SplObject $Spl
* @param int $Offset
*/function seek_spl($Spl, $Offset){
    if (version_compare(PHP_VERSION, '8.0.1', '>=') || $Offset == 0) {
        $Spl->seek($Offset);
    } else {
        if( $Offset == 1 ){
            $Spl->rewind(); // Ensure to go at first row before exit
            $Spl->fgets(); // Read line 0. Cursor remains now at line 1
        } else {
            $Spl->seek($Offset-1);
        }
    }
}?>

Comentarios

Artículos más populares

Clase en PHP para encender/apagar LED en Raspberry pi

Raspberry Pi. Encender/Apagar LED desde PHP. Acceso directo a los puertos GPIO ultrarápido.

Ja es poden veure els programes de 'REDES' de TVE2 per internet