strrpos does not work in PHP
Posted 2008-07-27 (Sun) 10:48 under Coding, Technical mumbo-jumbo
I use the PHP function strrpos in some of my code. Last night I discovered that it behaves very strangely. The fact that it has worked this far is a miracle.
The purpose of strrpos as opposed to strpos is to find the last occurance of a substring within a string and return its index. Pretty simple.
Let’s take a look at what happens in PHP 4.4.7 (which is the default version on my web host):
$url = 'http://www.yesasia.com/en/prdTransfer.aspx/pid-1003937129/'; $lastpos = strrpos($url, 'http://');
$lastpos is, as expected, set to zero.
Now, let’s try that again:
$url = 'http://www.yesasia.com/ai-am-best-album-dvd-hong-kong-version /1004779851-0-0-0-en/info.html'; $lastpos = strrpos($url, 'http://');
$lastpos will, of course, be zero now as well. No! It is set to 87!
Let’s see if we can determine what confuses the function:
$url = 'http://abc'; $lastpos = strrpos($url, 'http://'); // zero, as expected $url = 'http://abc.html'; $lastpos = strrpos($url, 'http://'); // 11, should be zero $url = 'http://www.yesasia.com/hal/0-aid32368-0-bpt.47-en/list.html'; $lastpos = strrpos($url, 'http://'); // 55, should be zero $url = 'http://www.yesasia.com/hal/0-aid32368-0-bptbanana47-en /listbanana'; $lastpos = strrpos($url, 'http://'); // 23, should be zero $url = 'www.yesasia.com_hal_0-aid32368-0-bptbanana47-en_listbanana'; $lastpos = strrpos($url, 'http://'); // 16, should be false
If I whip up two simple substitute functions, they work as expected every single time. I have no idea what’s going on inside strrpos!
function strrpos2($h, $n)
{
$lastpos = -1;
while(($pos = strpos($h, $n, $lastpos + 1)) !== false)
{
$lastpos = $pos;
}
if($lastpos == -1)
return false;
return $lastpos;
}
function strrpos3($h, $n)
{
$hrev = strrev($h);
$nrev = strrev($n);
$pos = strpos($hrev, $nrev);
if($pos === false)
return false;
$lastpos = $pos - strlen($h) + strlen($n);
return $lastpos;
}
$url = 'http://www.yesasia.com/ai-am-best-album-dvd-hong-kong-version
/1004779851-0-0-0-en/info.html';
$lastpos = strrpos($url, 'http://'); // 87
$lastpos = strrpos2($url, 'http://'); // zero
$lastpos = strrpos3($url, 'http://'); // zero
2008-07-28 (Mon) 10:59
funny
i tested your examples, but they worked good on my development system. ok, i am using the latest php5 on debian sid/unstable (php 5.2.6.something).
then i checked the documentation on php.net and i found that note:
“The needle may be a string of more than one character as of PHP 5.0.0.”
so now the behaviour of strrpos on your system makes sense, since it uses only the first character of your needle!
2008-07-29 (Tue) 20:29
Yeah, I figured it was something like that. It’s just that when I think about strrpos I think “strpos but reversed”, and by some extreme fluke all the examples I tried (earlier, when I first used it in my code) didn’t have any “h” except in “http”. The error only showed itself now, years later, when YesAsia changed the format of their product links.
I never saw that note in the documentation. Usually they put several examples to show the differences between PHP versions, like they do for strrpos earlier than PHP 4 and for PHP 4 and greater.
This is what I hate about typeless programming. If you send a string where a character is expected, a “typed” language would say “What the heck is this?!”, where PHP says “Although this isn’t at all what was expected, he probably meant the first character in the string, yeah let’s go with that”.
I have to search all my php code just to see that I haven’t used it like this somewhere else. I don’t think so, since I normally use it for characters, but better safe than sorry.