Michael Lenaghan Posted April 20 Share Posted April 20 Just reporting this to save other people some debugging time: Hanna Codes unintentionally "memoize" results. Here's a rough example of what I mean. Imagine a stateful Hanna Code. For example, imagine a Hanna Code that maintains a count, and increases the count each time it's used: [[count]] [[count]] [[count]] You'd expect this: 1 2 3 But you'll get this instead: 1 1 1 Here's where it gets very odd though: your Hanna Code will in fact be called three times, and the counter will in fact end up at "3"! It took me a bit to figure out what was going on. The problem is that all Hanna Codes are extracted up front, here. Then they're processed one by one, here. As each Hanna Code is processed, it gets replaced here. And that's the problem: the replacement is global; all matches get replaced, not just the first. So in this example all three `[[count]]`s will be extracted, and the loop will iterate three times, but the first iteration will replace all occurrences of `[[count]]`, leaving nothing for the next two iterations to replace. (I'm calling this "unintentional memoization" because typically you memoize to avoid work. In this case work isn't avoided, the result of the work simply isn't used.) 2 Link to comment Share on other sites More sharing options...
adrian Posted April 20 Share Posted April 20 Maybe "strtr" instead of "str_replace" would take care of things as it only replaces the first instance. I am surprised though that it's not mentioned at all here: https://stackoverflow.com/questions/1252693/using-str-replace-so-that-it-only-acts-on-the-first-match so maybe I am missing something? Perhaps this is worth a thorough read: https://stackoverflow.com/questions/8177296/when-to-use-strtr-vs-str-replace Link to comment Share on other sites More sharing options...
Michael Lenaghan Posted April 20 Author Share Posted April 20 Here is Laravel's implementation of `replaceFirst`; it uses `strpos` and `substr_replace`. I think the current behaviour is unintentional; I don't know if Ryan would consider it a bug? I suppose I should report it so that he can decide. 2 Link to comment Share on other sites More sharing options...
Michael Lenaghan Posted April 21 Author Share Posted April 21 Issue here, pull request here. 1 Link to comment Share on other sites More sharing options...
adrian Posted April 21 Share Posted April 21 @Michael Lenaghan - I am curious - did you find an issue with strtr or just went with Laravel's approach without looking into it? Not a criticism at all, just want it to be a learning experience for all of us if you did find an issue. Link to comment Share on other sites More sharing options...
Michael Lenaghan Posted April 21 Author Share Posted April 21 @adrian Take a look at the description of strtr: Quote strtr — Translate characters or replace substrings ... If given three arguments, this function returns a copy of string where all occurrences of each (single-byte) character in from have been translated to the corresponding character in to, i.e., every occurrence of $from[$n]has been replaced with $to[$n], where $n is a valid offset in both arguments. So the purpose of `strtr` is to replace individual characters, not substrings. Take a look at Example #3 to see what I mean. (The `tr` in `strtr` comes from the Unix command `tr`, which does the same thing.) Link to comment Share on other sites More sharing options...
Michael Lenaghan Posted April 21 Author Share Posted April 21 Sorry, I should explain a bit more! `strtr` can work in two modes, and in one mode it can replace substrings. But that isn't its purpose, and I think it would be confusing to use it for that. (It would be especially confusing if, like me, you happen to know about `tr`.) Even more, though: like `str_replace`, `strtr` replaces all matching strings, not just the first. So it can't actually solve the original problem anyway. 1 Link to comment Share on other sites More sharing options...
adrian Posted April 21 Share Posted April 21 Thanks for the explanations. I actually use strtr quite a lot for an array of substring replacements - works great. Sorry for the confusion about how it replaces - I wasn't thinking straight and was thinking about this difference: "strtr will not replace in parts of the string that already have been replaced - str_replace will replace inside replaces", rather than the replacing only the first occurrence - sorry I rushed without thinking it through - my bad :( Link to comment Share on other sites More sharing options...
Michael Lenaghan Posted April 21 Author Share Posted April 21 I don't know anything about the history of `strtr` in PHP, but my guess would be that the reason it has the ability to replace strings in the first place is simply an extension of its original purpose from single-byte characters to multi-byte. It just so happens that in PHP multi-byte characters are represented as strings… I don't know if that's right, but I've seen it in other languages, so it certainly seems plausible. And when you have that in your head using it to just replace arbitrary strings feels, you know, funny. ? 2 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now