Для позиционирования в файле нужно пользоваться
fseek а для получения ftell.
fgets смещает указатель на следующую строку, значит fwrite будет ее затирать. Значит если у тебя была последняя строка - значит указатель будет в конце файла.
Теперь о главном - замена строки в текстовом файле нормально будет работать только если размер этой строки совпадает с прошлой, иначе при меньшем размере новой строки в файле останется мусор от прошлой, а при большем - будут затерты следующие строки. Т.е. понадобится сдвигать содержимое всего файла после текущей позиции или полностью перезаписывать весь файл, причем если оперативной памяти хватает то это легко - загрузил построчно file, изменил нужную строку в массиве, и сохранил с помощью к примеру implode+file_put_contents или циклом по массиву и fputs.
Именно по этой причине
текстовые форматы данных не подходят для хранения нескольких записей. Допускается по одному файлу на запись, а запись это текстовая строка.
Благодаря обширному количеству поддержки различной сериализации данных в текстовый формат, и не только (var_export/serialize/json_encode/
igbinary), полная перезапись файла все еще используется как способ хранения миниатюрной базы данных в файле, но только потому что код для его использования очень простой, пока не нужно думать о многопоточном доступе к этим данным.
Настоятельно рекомендуется не изобретать паровоз и пользоваться например
sqlite, универсальная функциональная sql база данных, не требующая обслуживания и установки (работает на основе файла без подключения, как библиотека).