Ali
Ali

Reputation: 267049

startsWith() and endsWith() functions in PHP

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

Answers (30)

dkellner
dkellner

Reputation: 9896

Note:

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.

So here's a tiny improvement to startsWith:

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

As for strings vs integers

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

Toby Allen
Toby Allen

Reputation: 11213

Laravel

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

redanimalwar
redanimalwar

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

Salman Arshad
Salman Arshad

Reputation: 272006

PHP < 8

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

MrHus
MrHus

Reputation: 33378

PHP 8.0 and higher

Since PHP 8.0 you can use:

str_starts_with and
str_ends_with

Example
var_dump(str_starts_with('|apples}', '|'));
var_dump(str_ends_with('|apples}', '}'));

PHP before 8.0

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

Piyin
Piyin

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

Maxim Rysevets
Maxim Rysevets

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

JSON
JSON

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

Jon
Jon

Reputation: 437326

PHP 8 update

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

Lucas Bustamante
Lucas Bustamante

Reputation: 17168

Fastest endsWith() solution:

# Checks if a string ends in a string
function endsWith($haystack, $needle) {
    return substr($haystack,-strlen($needle))===$needle;
}

Benchmark:

# 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();

Benchmark Results:

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

lepe
lepe

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

mazatwork
mazatwork

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

ya.teck
ya.teck

Reputation: 2336

You can use the fnmatch function for this.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);

Upvotes: 0

Vahid Amiri
Vahid Amiri

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

noamtm
noamtm

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

ymakux
ymakux

Reputation: 3485

$ends_with = strrchr($text, '.'); // Ends with dot
$start_with = (0 === strpos($text, '.')); // Starts with dot

Upvotes: -1

mpen
mpen

Reputation: 282775

Updated 23-Aug-2016

Functions

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;
}

Tests

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";
}

Results (PHP 7.0.9)

(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

Results (PHP 5.3.29)

(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

startswith_benchmark.php

Upvotes: 262

Veeno
Veeno

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

Lex Podgorny
Lex Podgorny

Reputation: 2930

You can use strpos and strrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);

Upvotes: 12

Sander Rijken
Sander Rijken

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

Patrick Smith
Patrick Smith

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

yuvilio
yuvilio

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

womplefrog
womplefrog

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

Jelle Keizer
Jelle Keizer

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

Bill Effin Murray
Bill Effin Murray

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

Ja͢ck
Ja͢ck

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

Srinivasan.S
Srinivasan.S

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

KdgDev
KdgDev

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

FrancescoMM
FrancescoMM

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

user507410
user507410

Reputation: 512

This may work

function startsWith($haystack, $needle) {
     return substr($haystack, 0, strlen($needle)) == $needle;
}

Source: https://stackoverflow.com/a/4419658

Upvotes: 5

Related Questions