Exact c'est strrpos qu'il faut utiliser, je cherchais un truc du genre lastindexof dans mon glossaire php, et je ne l'avais pas trouvé du coup (il faudrait normaliser les noms de ces genres de fonctions) mais c'est clairement ce qu'il y a de mieux.
D'autant plus que suivant les valeurs de $length (ok là il fait 200 mais bon) ton algo peut planter.
Je pensais aussi à ce qu'il pourrait se passer si ton dernier mot faisait plus de 15 caractères: ok tu va me dire un mot de plus de 15 lettres ça traîne pas les rues, n'empêche que dans le cadre d'une énigme cryptée, c'est envisageable. Ça ne fait pas planter l'algo, ça le fait juste tourner pour rien et un substring = une nouvelle instance de chaîne. Ensuite, fais le compte: un chaîne de n chars (2000 par exemple), puis 17 chaînes de 200 chars, ça fait un bon paquet alloué et je ne pense pas le plus grand bien du "garbage collector" de php. Autrement dit, à éviter.
Enfin, une dernière piste à explorer: essaye de faire toute ta cuisine sur les chaînes (formatages, remplacements, troncature, ...) directement au niveau du select (même MySql propose des fonctions pour ça, on est loin du SQL analytique d'Oracle mais bon c'est suffisant).
En esperant avoir été utile.