I wrote:
Thanks to everyone for your comments. It seems my own intuition on this more or less agrees with what everyone was saying. The code I committed before this thread took off was more or less going in the right direction, although there are some places where exceptions have been used as a stopgap, and they should probably be migrated to use return values at some stage.
Maybe I should add that the reason I was unsure was because of the common practice of standard libraries in languages such as Java to throw exceptions at the drop of a hat, thus forcing the programmer to either inefficiently duplicate validity checks on parameters before calling, or to put try/catch blocks around every damn call.
That's not to say I support PHP's error model unconditionally, where unnecessary warnings encourage programmers to use the incredibly evil @ operator. But at least you can find a substring of a string without having exceptions thrown at you. I guess that's an advantage of weak typing more than anything -- PHP consistently uses specially typed returns to indicate errors.
-- Tim Starling