Reputation: 267049
How can I write two functions that would take a string and return if it starts with the specified character/string or ends with it?
For example:
$str = '|apples}';
echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Upvotes: 1704
Views: 1000953
Reputation: 9896
It's 2024, we have better options how, namely
str_starts_with
is exactly for this job. This article is only here for two reasons, 1: to support older php versions (7.x), legacy code, wp plugins, etc, and 2: because I like it.
function startsWith($haystack,$needle) {
if($needle==="") return true;
if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}
That extra line, comparing the first character of the strings, can make the false case return immediately, therefore making many of your comparisons a lot faster (7x faster when I measured). In the true case you pay virtually no price in performance for that single line so I think it's worth including. (Also, in practice, when you test many strings for a specific starting chunk, most comparisons will fail since in a typical case you're looking for something.)
NOTE: the bug in @Tino's comment below has aleady been fixed
If you want to force string comparison (that is, you expect startsWith("1234",12) to be true), you'll need some typecasting:
function startsWith($haystack,$needle) {
if($needle==="") return true;
$haystack = (string)$haystack;
$needle = (string)$needle;
if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}
I don't think it's necessary but it's an interesting edge case, leading to questions like "does boolean true begin with a t?" - so you decide, but make sure you decide for good.
Upvotes: 6
Reputation: 11213
If you are using Laravel then you can do the following (if you are not using Laravel, you really should be).
Str::of('a long string')->startsWith('a');
Str::of('a long string')->endsWith('string');
//true
//true
Upvotes: 0
Reputation: 1503
Let me give a 2024 answer to this that I think still brings value.
I created a polyfill based on Symfony code that I think better that most of the stuff posted here.
<?php
/**
* These are based on symfony/polyfill-php80. The aim of this is to have a minimalistic polyfill
* for only the str_* functions of PHP 8.0. Requires php 7.1 or higher.
*
* @link https://github.com/symfony/polyfill-php80/blob/1.x/bootstrap.php
* @link https://github.com/symfony/polyfill-php80/blob/1.x/Php80.php
*
* @license GPL-3.0
* @copyright (c) Fabien Potencier <[email protected]>, (c) 2024 Nicolas Jonas nextgenthemes.com
*
* @author Ion Bazan <[email protected]>
* @author Nico Oelgart <[email protected]>
* @author Nicolas Grekas <[email protected]>
* @author Nicolas Jonas nextgenthemes.com
*/
// < PHP 7.1
if ( \PHP_VERSION_ID < 70100 ) {
exit( 'The str_contains, str_starts_with and str_ends_with polyfills require PHP 7.1 or higher.' );
}
// >= PHP 8.0
if ( \PHP_VERSION_ID >= 80000 ) {
return;
}
if ( ! function_exists('str_contains') ) {
function str_contains( ?string $haystack, ?string $needle ): bool {
$haystack = $haystack ?? '';
$needle = $needle ?? '';
return '' === $needle || false !== strpos($haystack, $needle);
}
}
if ( ! function_exists('str_starts_with') ) {
function str_starts_with( ?string $haystack, ?string $needle ): bool {
$haystack = $haystack ?? '';
$needle = $needle ?? '';
return 0 === strncmp($haystack, $needle, \strlen($needle));
}
}
if ( ! function_exists('str_ends_with') ) {
function str_ends_with( ?string $haystack, ?string $needle ): bool {
$haystack = $haystack ?? '';
$needle = $needle ?? '';
if ( '' === $needle || $needle === $haystack ) {
return true;
}
if ( '' === $haystack ) {
return false;
}
$needle_length = \strlen($needle);
return $needle_length <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needle_length);
}
}
Upvotes: 0
Reputation: 272006
You can use substr_compare
function to check start-with and ends-with:
function startsWith($haystack, $needle) {
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
function endsWith($haystack, $needle) {
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
This should be one of the fastest solutions on PHP 7 (benchmark script). Tested against 8KB haystacks, various length needles and full, partial and no match cases. strncmp
is a touch faster for starts-with but it cannot check ends-with.
Upvotes: 1106
Reputation: 33378
Since PHP 8.0 you can use:
str_starts_with
and
str_ends_with
var_dump(str_starts_with('|apples}', '|'));
var_dump(str_ends_with('|apples}', '}'));
function startsWith( $haystack, $needle ) {
$length = strlen( $needle );
return substr( $haystack, 0, $length ) === $needle;
}
function endsWith( $haystack, $needle ) {
$length = strlen( $needle );
if( !$length ) {
return true;
}
return substr( $haystack, -$length ) === $needle;
}
Upvotes: 1872
Reputation: 1834
I know I'm really late to the party, but I had to use PHP 7
and made it work with explode
, which looks really simple:
function startsWith($haystack, $needle) {
$cutsQuantity = 2;
$parts = explode($needle, $haystack, $cutsQuantity);
return ($parts[0] === '');
}
function endsWith($haystack, $needle) {
$parts = explode($needle, $haystack);
return (array_pop($parts) === '');
}
var_dump(startsWith('The quick brown fox jumps over the lazy dog', 'The quick')); // true
var_dump(startsWith('The quick brown fox jumps over the lazy dog', 'lazy dog')); // false
var_dump(endsWith('The quick brown fox jumps over the lazy dog', 'The quick')); // false
var_dump(endsWith('The quick brown fox jumps over the lazy dog', 'lazy dog')); // true
According to 3v4l.org
this should work from PHP 4.3.0
: https://3v4l.org/gRggY#v4.3.0
Upvotes: 0
Reputation: 147
I had the task of creating the most approximate polyfill of str_starts_with() for the open CMS EffCore and the result was the following code:
namespace {
if (!function_exists('str_starts_with')) {
function str_starts_with($haystack, $needle) {
if (is_null ($haystack)) trigger_error('str_starts_with(): Passing null to parameter #1 ($haystack) of type string is deprecated in '.__FILE__, E_USER_DEPRECATED);
if (is_null ($needle) ) trigger_error('str_starts_with(): Passing null to parameter #2 ($needle) of type string is deprecated in '.__FILE__, E_USER_DEPRECATED);
if (is_array ($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, array given');
if (is_object ($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, object given');
if (is_resource($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, resource given');
if (is_array ($needle) ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, array given');
if (is_object ($needle) ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, object given');
if (is_resource($needle) ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, resource given');
if ((string)$needle === '') return true;
return strpos((string)$haystack, (string)$needle) === 0;
}
}
}
Test data:
str_starts_with('100', '') # true
str_starts_with('100', '1') # true
str_starts_with('100', '0') # false
str_starts_with('100', 0) # false
str_starts_with('100', 1) # true
str_starts_with('100', 0.0) # false
str_starts_with('100', 1.0) # true
str_starts_with('100', true) # true / (string)true === '1'
str_starts_with('100', false) # true / (string)false === ''
str_starts_with('100', null) # Warning
str_starts_with('100', []) # Exception
str_starts_with('100', [0]) # Exception
str_starts_with('100', [1]) # Exception
str_starts_with('100', new stdCLass) # Exception
str_starts_with('100', new SomeClass) # Exception
str_starts_with('100', fopen('resource') # Exception
str_starts_with('010', '') # true
str_starts_with('010', '1') # false
str_starts_with('010', '0') # true
str_starts_with('010', 0) # true
str_starts_with('010', 1) # false
str_starts_with('010', 0.0) # true
str_starts_with('010', 1.0) # false
str_starts_with('010', true) # false / (string)true === '1'
str_starts_with('010', false) # true / (string)false === ''
str_starts_with('010', null) # Warning
str_starts_with('010', []) # Exception
str_starts_with('010', [0]) # Exception
str_starts_with('010', [1]) # Exception
str_starts_with('010', new stdCLass) # Exception
str_starts_with('010', new SomeClass) # Exception
str_starts_with('010', fopen('resource') # Exception
str_starts_with('', '1') # false
str_starts_with('100', '1') # true
str_starts_with('010', '1') # false
str_starts_with('001', '1') # false
str_starts_with(0, '1') # false
str_starts_with(1, '1') # true
str_starts_with(0.0, '1') # false
str_starts_with(1.0, '1') # true
str_starts_with(true, '1') # true / (string)true === '1'
str_starts_with(false, '1') # false / (string)false === ''
str_starts_with(null, '1') # Warning
str_starts_with([], '1') # Exception
str_starts_with([0], '1') # Exception
str_starts_with([1], '1') # Exception
str_starts_with(new stdCLass, '1') # Exception
str_starts_with(new SomeClass, '1') # Exception
str_starts_with(fofopen('resource'), '1') # Exception
Upvotes: 0
Reputation: 1835
function startsWith($haystack, $needle)
{
$length = mb_strlen($needle);
return mb_substr($haystack, 0, $length) === $needle;
}
function endsWith($haystack, $needle)
{
$length = mb_strlen($needle);
if(!$length)
{
return true;
}
return mb_substr($haystack, -$length) === $needle;
}
String length and character positioning are two examples where multi-byte vs single-byte can play a difference. Standard strings in PHP are char arrays just as in C, which are always single byte. so the third character in a string (str[2]
) by normal strlen would actually be the start of the second character if UTF-16. If looking for {
at the beginning of a string to see if a string could be JSON this wouldn't be a huge deal since {
is a valid single byte character such as in ASCII encoding, but if checking the end of a string for the emogi 👍 vs 😲 to judge reactions it's a different story since they are multipbyte characters.
Upvotes: 0
Reputation: 437326
PHP 8 includes new str_starts_with
and str_ends_with
functions that finally provide a performant and convenient solution to this problem:
$str = "beginningMiddleEnd";
if (str_starts_with($str, "beg")) echo "printed\n";
if (str_starts_with($str, "Beg")) echo "not printed\n";
if (str_ends_with($str, "End")) echo "printed\n";
if (str_ends_with($str, "end")) echo "not printed\n";
The RFC for this feature provides more information, and also a discussion of the merits and problems of obvious (and not-so-obvious) userland implementations.
Upvotes: 54
Reputation: 17168
# Checks if a string ends in a string
function endsWith($haystack, $needle) {
return substr($haystack,-strlen($needle))===$needle;
}
# This answer
function endsWith($haystack, $needle) {
return substr($haystack,-strlen($needle))===$needle;
}
# Accepted answer
function endsWith2($haystack, $needle) {
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}
# Second most-voted answer
function endsWith3($haystack, $needle) {
// search forward starting from end minus needle length characters
if ($needle === '') {
return true;
}
$diff = \strlen($haystack) - \strlen($needle);
return $diff >= 0 && strpos($haystack, $needle, $diff) !== false;
}
# Regex answer
function endsWith4($haystack, $needle) {
return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}
function timedebug() {
$test = 10000000;
$time1 = microtime(true);
for ($i=0; $i < $test; $i++) {
$tmp = endsWith('TestShortcode', 'Shortcode');
}
$time2 = microtime(true);
$result1 = $time2 - $time1;
for ($i=0; $i < $test; $i++) {
$tmp = endsWith2('TestShortcode', 'Shortcode');
}
$time3 = microtime(true);
$result2 = $time3 - $time2;
for ($i=0; $i < $test; $i++) {
$tmp = endsWith3('TestShortcode', 'Shortcode');
}
$time4 = microtime(true);
$result3 = $time4 - $time3;
for ($i=0; $i < $test; $i++) {
$tmp = endsWith4('TestShortcode', 'Shortcode');
}
$time5 = microtime(true);
$result4 = $time5 - $time4;
echo $test.'x endsWith: '.$result1.' seconds # This answer<br>';
echo $test.'x endsWith2: '.$result4.' seconds # Accepted answer<br>';
echo $test.'x endsWith3: '.$result2.' seconds # Second most voted answer<br>';
echo $test.'x endsWith4: '.$result3.' seconds # Regex answer<br>';
exit;
}
timedebug();
10000000x endsWith: 1.5760900974274 seconds # This answer
10000000x endsWith2: 3.7102129459381 seconds # Accepted answer
10000000x endsWith3: 1.8731069564819 seconds # Second most voted answer
10000000x endsWith4: 2.1521229743958 seconds # Regex answer
Upvotes: 33
Reputation: 25200
If speed is important for you, try this.(I believe it is the fastest method)
Works only for strings and if $haystack is only 1 character
function startsWithChar($needle, $haystack)
{
return ($needle === $haystack[0]);
}
function endsWithChar($needle, $haystack)
{
return ($needle === $haystack[strlen($haystack) - 1]);
}
$str='|apples}';
echo startsWithChar('|',$str); //Returns true
echo endsWithChar('}',$str); //Returns true
echo startsWithChar('=',$str); //Returns false
echo endsWithChar('#',$str); //Returns false
Upvotes: 29
Reputation: 1305
No-copy and no-intern-loop:
function startsWith(string $string, string $start): bool
{
return strrpos($string, $start, - strlen($string)) !== false;
}
function endsWith(string $string, string $end): bool
{
return ($offset = strlen($string) - strlen($end)) >= 0
&& strpos($string, $end, $offset) !== false;
}
Upvotes: 3
Reputation: 2336
You can use the fnmatch function for this.
// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);
Upvotes: 0
Reputation: 11107
Here's a multi-byte safe version of the accepted answer, it works fine for UTF-8 strings:
function startsWith($haystack, $needle)
{
$length = mb_strlen($needle, 'UTF-8');
return (mb_substr($haystack, 0, $length, 'UTF-8') === $needle);
}
function endsWith($haystack, $needle)
{
$length = mb_strlen($needle, 'UTF-8');
return $length === 0 ||
(mb_substr($haystack, -$length, $length, 'UTF-8') === $needle);
}
Upvotes: 15
Reputation: 12953
This question already has many answers, but in some cases you can settle for something simpler than all of them. If the string you're looking for is known (hardcoded), you can use regular expressions without any quoting etc.
Check if a string starts with 'ABC':
preg_match('/^ABC/', $myString); // "^" here means beginning of string
ends with 'ABC':
preg_match('/ABC$/', $myString); // "$" here means end of string
In my simple case, I wanted to check if a string ends with slash:
preg_match('#/$#', $myPath); // Use "#" as delimiter instead of escaping slash
The advantage: since it's very short and simple, you don't have to define a function (such as endsWith()
) as shown above.
But again -- this is not a solution for every case, just this very specific one.
Upvotes: 48
Reputation: 3485
$ends_with = strrchr($text, '.'); // Ends with dot
$start_with = (0 === strpos($text, '.')); // Starts with dot
Upvotes: -1
Reputation: 282775
Updated 23-Aug-2016
function substr_startswith($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) === $needle;
}
function preg_match_startswith($haystack, $needle) {
return preg_match('~' . preg_quote($needle, '~') . '~A', $haystack) > 0;
}
function substr_compare_startswith($haystack, $needle) {
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
function strpos_startswith($haystack, $needle) {
return strpos($haystack, $needle) === 0;
}
function strncmp_startswith($haystack, $needle) {
return strncmp($haystack, $needle, strlen($needle)) === 0;
}
function strncmp_startswith2($haystack, $needle) {
return $haystack[0] === $needle[0]
? strncmp($haystack, $needle, strlen($needle)) === 0
: false;
}
echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
if($i % 2500 === 0) echo '.';
$test_cases[] = [
random_bytes(random_int(1, 7000)),
random_bytes(random_int(1, 3000)),
];
}
echo "done!\n";
$functions = ['substr_startswith', 'preg_match_startswith', 'substr_compare_startswith', 'strpos_startswith', 'strncmp_startswith', 'strncmp_startswith2'];
$results = [];
foreach($functions as $func) {
$start = microtime(true);
foreach($test_cases as $tc) {
$func(...$tc);
}
$results[$func] = (microtime(true) - $start) * 1000;
}
asort($results);
foreach($results as $func => $time) {
echo "$func: " . number_format($time, 1) . " ms\n";
}
(Sorted fastest to slowest)
strncmp_startswith2: 40.2 ms
strncmp_startswith: 42.9 ms
substr_compare_startswith: 44.5 ms
substr_startswith: 48.4 ms
strpos_startswith: 138.7 ms
preg_match_startswith: 13,152.4 ms
(Sorted fastest to slowest)
strncmp_startswith2: 477.9 ms
strpos_startswith: 522.1 ms
strncmp_startswith: 617.1 ms
substr_compare_startswith: 706.7 ms
substr_startswith: 756.8 ms
preg_match_startswith: 10,200.0 ms
Upvotes: 262
Reputation: 113
The answer by mpen is incredibly thorough, but, unfortunately, the provided benchmark has a very important and detrimental oversight.
Because every byte in needles and haystacks is completely random, the probability that a needle-haystack pair will differ on the very first byte is 99.609375%, which means that, on average, about 99609 of the 100000 pairs will differ on the very first byte. In other words, the benchmark is heavily biased towards startswith
implementations which check the first byte explicitly, as strncmp_startswith2
does.
If the test-generating loop is instead implemented as follows:
echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
if($i % 2500 === 0) echo '.';
$haystack_length = random_int(1, 7000);
$haystack = random_bytes($haystack_length);
$needle_length = random_int(1, 3000);
$overlap_length = min(random_int(0, $needle_length), $haystack_length);
$needle = ($needle_length > $overlap_length) ?
substr($haystack, 0, $overlap_length) . random_bytes($needle_length - $overlap_length) :
substr($haystack, 0, $needle_length);
$test_cases[] = [$haystack, $needle];
}
echo " done!<br />";
the benchmark results tell a slightly different story:
strncmp_startswith: 223.0 ms
substr_startswith: 228.0 ms
substr_compare_startswith: 238.0 ms
strncmp_startswith2: 253.0 ms
strpos_startswith: 349.0 ms
preg_match_startswith: 20,828.7 ms
Of course, this benchmark may still not be perfectly unbiased, but it tests the efficiency of the algorithms when given partially matching needles as well.
Upvotes: 8
Reputation: 2930
You can use strpos
and strrpos
$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Upvotes: 12
Reputation: 21615
All answers so far seem to do loads of unnecessary work, strlen calculations
, string allocations (substr)
, etc. The 'strpos'
and 'stripos'
functions return the index of the first occurrence of $needle
in $haystack
:
function startsWith($haystack,$needle,$case=true)
{
if ($case)
return strpos($haystack, $needle, 0) === 0;
return stripos($haystack, $needle, 0) === 0;
}
function endsWith($haystack,$needle,$case=true)
{
$expectedPosition = strlen($haystack) - strlen($needle);
if ($case)
return strrpos($haystack, $needle, 0) === $expectedPosition;
return strripos($haystack, $needle, 0) === $expectedPosition;
}
Upvotes: 147
Reputation: 537
Here’s an efficient solution for PHP 4. You could get faster results if on PHP 5 by using substr_compare
instead of strcasecmp(substr(...))
.
function stringBeginsWith($haystack, $beginning, $caseInsensitivity = false)
{
if ($caseInsensitivity)
return strncasecmp($haystack, $beginning, strlen($beginning)) === 0;
else
return strncmp($haystack, $beginning, strlen($beginning)) === 0;
}
function stringEndsWith($haystack, $ending, $caseInsensitivity = false)
{
if ($caseInsensitivity)
return strcasecmp(substr($haystack, strlen($haystack) - strlen($ending)), $haystack) === 0;
else
return strpos($haystack, $ending, strlen($haystack) - strlen($ending)) !== false;
}
Upvotes: 2
Reputation: 4175
I usually end up going with a library like underscore-php these days.
require_once("vendor/autoload.php"); //use if needed
use Underscore\Types\String;
$str = "there is a string";
echo( String::startsWith($str, 'the') ); // 1
echo( String::endsWith($str, 'ring')); // 1
The library is full of other handy functions.
Upvotes: 8
Reputation: 799
Many of the previous answers will work just as well. However, this is possibly as short as you can make it and have it do what you desire. You just state that you'd like it to 'return true'. So I've included solutions that returns boolean true/false and the textual true/false.
// boolean true/false
function startsWith($haystack, $needle)
{
return strpos($haystack, $needle) === 0 ? 1 : 0;
}
function endsWith($haystack, $needle)
{
return stripos($haystack, $needle) === 0 ? 1 : 0;
}
// textual true/false
function startsWith($haystack, $needle)
{
return strpos($haystack, $needle) === 0 ? 'true' : 'false';
}
function endsWith($haystack, $needle)
{
return stripos($haystack, $needle) === 0 ? 'true' : 'false';
}
Upvotes: 3
Reputation: 721
I would do it like this
function startWith($haystack,$needle){
if(substr($haystack,0, strlen($needle))===$needle)
return true;
}
function endWith($haystack,$needle){
if(substr($haystack, -strlen($needle))===$needle)
return true;
}
Upvotes: 4
Reputation: 436
Why not the following?
//How to check if a string begins with another string
$haystack = "valuehaystack";
$needle = "value";
if (strpos($haystack, $needle) === 0){
echo "Found " . $needle . " at the beginning of " . $haystack . "!";
}
Output:
Found value at the beginning of valuehaystack!
Keep in mind, strpos
will return false if the needle was not found in the haystack, and will return 0 if, and only if, needle was found at index 0 (AKA the beginning).
And here's endsWith:
$haystack = "valuehaystack";
$needle = "haystack";
//If index of the needle plus the length of the needle is the same length as the entire haystack.
if (strpos($haystack, $needle) + strlen($needle) === strlen($haystack)){
echo "Found " . $needle . " at the end of " . $haystack . "!";
}
In this scenario there is no need for a function startsWith() as
(strpos($stringToSearch, $doesItStartWithThis) === 0)
will return true or false accurately.
It seems odd it's this simple with all the wild functions running rampant here.
Upvotes: 4
Reputation: 173522
Here are two functions that don't introduce a temporary string, which could be useful when needles are substantially big:
function startsWith($haystack, $needle)
{
return strncmp($haystack, $needle, strlen($needle)) === 0;
}
function endsWith($haystack, $needle)
{
return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
Upvotes: 23
Reputation: 3143
I hope that the below answer may be efficient and also simple:
$content = "The main string to search";
$search = "T";
//For compare the begining string with case insensitive.
if(stripos($content, $search) === 0) echo 'Yes';
else echo 'No';
//For compare the begining string with case sensitive.
if(strpos($content, $search) === 0) echo 'Yes';
else echo 'No';
//For compare the ending string with case insensitive.
if(stripos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
//For compare the ending string with case sensitive.
if(strpos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
Upvotes: 7
Reputation: 14529
function startsWith($haystack, $needle, $case = true) {
if ($case) {
return (strcmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
}
return (strcasecmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
}
function endsWith($haystack, $needle, $case = true) {
if ($case) {
return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
}
return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
}
Credit To:
Check if a string ends with another string
Check if a string begins with another string
Upvotes: 54
Reputation: 2960
Focusing on startswith, if you are sure strings are not empty, adding a test on the first char, before the comparison, the strlen, etc., speeds things up a bit:
function startswith5b($haystack, $needle) {
return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;
}
It is somehow (20%-30%) faster. Adding another char test, like $haystack{1}===$needle{1} does not seem to speedup things much, may even slow down.
===
seems faster than ==
Conditional operator (a)?b:c
seems faster than if(a) b; else c;
For those asking "why not use strpos?" calling other solutions "unnecessary work"
strpos is fast, but it is not the right tool for this job.
To understand, here is a little simulation as an example:
Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c
What the computer does "inside"?
With strccmp, etc...
is a===b? NO
return false
With strpos
is a===b? NO -- iterating in haysack
is a===c? NO
is a===d? NO
....
is a===g? NO
is a===g? NO
is a===a? YES
is 1===1? YES -- iterating in needle
is 2===3? YES
is 4===4? YES
....
is 8===8? YES
is c===x? NO: oh God,
is a===1? NO -- iterating in haysack again
is a===2? NO
is a===3? NO
is a===4? NO
....
is a===x? NO
is a===b? NO
is a===b? NO
is a===b? NO
is a===b? NO
is a===b? NO
is a===b? NO
is a===b? NO
...
... may many times...
...
is a===b? NO
is a===a? YES -- iterating in needle again
is 1===1? YES
is 2===3? YES
is 4===4? YES
is 8===8? YES
is c===c? YES YES YES I have found the same string! yay!
was it at position 0? NOPE
What you mean NO? So the string I found is useless? YEs.
Damn.
return false
Assuming strlen does not iterate the whole string (but even in that case) this is not convenient at all.
Upvotes: 10
Reputation: 512
This may work
function startsWith($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) == $needle;
}
Source: https://stackoverflow.com/a/4419658
Upvotes: 5