Mon, 13/10/2008 - 22:39 — horuskol
Source code: Function astrpos()
<?php /** * function astrpos() * * Function to find the position of the first instance of any needle * from an array of strings within a string * * * @author Stuart Jones <stuart@random-tweak.co.uk> * @copyright Copyright 2008 Stuart Jones * @version 08.10.13 * @license <a href="http://www.gnu.org/licenses/gpl.html<br /> " title="http://www.gnu.org/licenses/gpl.html<br /> ">http://www.gnu.org/licenses/gpl.html<br /> </a> * * @param string $haystack * @param array<string> $needles * @param [int $offset] * @param [int $flags] */ // define constants for function flags if (!defined('ASTR_NEEDLE_ORDER')) { define('ASTR_NEEDLE_ORDER', 1); } if (!defined('ASTR_STRING_ORDER')) { define('ASTR_STRING_ORDER', 2); } function astrpos($haystack, $needles, $offset = 0, $flags = 0) { // some error handling for incoming values $backtrace = debug_backtrace(); if (!is_string($haystack)) { trigger_error('Invalid argument 1 for ' . __FUNCTION__ . '() - expecting string, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } if (empty($haystack)) { trigger_error('Invalid argument 1 for ' . __FUNCTION__ . '() - cannot be an empty string, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } if (!is_array($needles)) { trigger_error('Invalid argument 2 for ' . __FUNCTION__ . '() - expecting array, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } if (empty($needles)) { trigger_error('Invalid argument 2 for ' . __FUNCTION__ . '() - cannot be an empty array, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } if (!is_int($offset) || $offset < 0) { trigger_error('Invalid argument 3 for ' . __FUNCTION__ . '() - expecting positive integer, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } if (!is_int($flags) || $flags < 0) { trigger_error('Invalid argument 3 for ' . __FUNCTION__ . '() - expecting positive integer, called in ' . $backtrace[0]['file'] . ' on line ' . $backtrace[0]['line'] . ' and defined', E_USER_WARNING); return false; } // the bit that does the work $astrpos = false; foreach ($needles as $needle) { // cycle through the needles $strpos = false; if (($strpos = strpos($haystack, $needle, $offset)) !== false) { if ($flags && ASTR_NEEDLE_ORDER) { return $strpos; } if (($astrpos > $strpos) || ($astrpos === false)) { $astrpos = $strpos; } } } return $astrpos; } ?>
Licence
The program/snippet/script provided in this post is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
Comments
Last comment on this one for
Last comment on this one for today I swear :)
I took your implementation and stripped out the validation, and profiled the two side-by-side.
As it turns out, my implementation is over 5 times slower! So there you go…
Thanks for taking the time to
Thanks for taking the time to speed-test the function – I’m not too surprised that strpos is faster than the preg, though.
I also might work up a multibyte version over the weekend – the logic should be about the same. I could do it as an extra flag to save having to create another function entirely (one thing that I find slightly irritating about PHP is the explosion of functions with only minor differences in behaviour – the other is the inconsistent naming practices).
And I think you’re right about the && – I’ll clear that up in my source at home and update the blogpost tonight.
Thanks again.
Also, I think this line: if
Also, I think this line:
should read:
otherwise the function will use needle order if you pass anything that evaluates to a Boolean true as the flag parameter.
If you can deal with losing
If you can deal with losing the ASTR_NEEDLE_ORDER functionality and some of the validation, this will do the same thing:
The point is that I wanted
The point is that I wanted the ASTR_NEEDLE_ORDER.
Also, the validation is there for good practice – if I was writing this script for something quick and dirty, then I wouldn’t bother – it is only because this is something that I have posted for other people to use and wanted it to behave in a similar fashion to how the PHP native functions did when it was passed bad parameters. With the validation, you get an error at the function call to astrpos(), not at one of the internal calls.
Both valid points absolutely.
Both valid points absolutely. Although technically you could quite easily apply the same validation checks to my version.
What would be interesting, I think, would be to test the relative speed of the two implementations. Given that all the iteration in my version is pushed off onto native PHP functions, I would expect it to be somewhat faster. That said, preg_ functions are not the fastest in the PHP arsenal.
One thing I reckon you do need to check; your implementation uses regular strpos instead of multibyte-safe mb_strpos. You may have intended this, but other users would probably expect multibyte compatibility.