2

I've only ever seen developers use strict comparisons when they use strpos to check for the existence of a substring:

if (strpos($haystack,$needle) !== false) {

}

It occurred to me today that one could use is_numeric ins

if (is_numeric(strpos($haystack,$needle))) {

}

Is there a reason one would use one over the other (specifically in this use case)?

If you think about it, the purpose of strpos is to return the position of the substring. Only if it doesn't exist, will it return false. The position is a number. Thus, is_numeric is pretty qualified to be considered semantically.

Nathan J.B.
  • 10,215
  • 3
  • 33
  • 41

5 Answers5

3

I've created a benchmark. Case check by compare with false value always faster than check by is_numeric.

// Init a big string array

for($i = 0; $i < 10000; $i++){
$str[] = 'a'.rand().'b'.rand();
}

// Case comparing with false value
$time1 = microtime(true);
foreach($str as $st){
$res[] = strpos($st,rand(0, count(array('b', 'c')) - 1 )) !== false;
}
$time2 = microtime(true);

echo $time2-$time1.'<br/>';

// Case 'is_numeric'
$time3 = microtime(true);
foreach($str as $st){
$res[] = is_numeric(strpos($st,rand(0, count(array('b', 'c')) - 1 )));
}
$time4 = microtime(true);
echo $time4-$time3;


//Time 1: 
//0.018877029418945 
//0.020556926727295

//Time 2:
//0.016352891921997
//0.016934871673584

//Time 3:
//0.0121009349823
//0.01330304145813

//Time 4:
//0.017507076263428
//0.01904296875
Vuong
  • 617
  • 11
  • 26
2

I found this question searching for almost the same question, except my thought was to use is_int( ) not is_numeric( ). I couldn't really understand the benchmark that was posted so I made my own. In short, is_numeric( ) is a hair slower than !== false, and is_int( ) is a hair faster. Personally, I find it more readable than !== false not less, so I'll probably switch to it.

This was done under php v7.1.23

Results:

0.7900s for '!== false' with hits
4.5137s for '!== false' with misses

0.9297s for 'is_numeric' with hits
4.7509s for 'is_numeric' with misses

0.6391s for 'is_int' with hits
4.4862s for 'is_int' with misses

From the code:

$n_times = 10000000;
$haystack_hit = "  Certificate Name: example.com";
$heystack_miss = "Found the following certs:";
$needle = "Certificate Name:";

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if (strpos($haystack_hit,$needle) !== false)
        { }
    }
$end = microtime(true) - $start;
echo "\n" . round($end,4) . "s for '!== false' with hits\n";

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if (strpos($haystack_miss,$needle) !== false)
        { }
    }
$end = microtime(true) - $start;
echo round($end,4) . "s for '!== false' with misses\n\n";

// -----

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if ( is_numeric(strpos($haystack_hit,$needle)) )
        { }
    }
$end = microtime(true) - $start;
echo round($end,4) . "s for 'is_numeric' with hits\n";

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if ( is_numeric(strpos($haystack_miss,$needle)) )
        { }
    }
$end = microtime(true) - $start;
echo round($end,4) . "s for 'is_numeric' with misses\n\n";

// -----

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if ( is_int(strpos($haystack_hit,$needle)) )
        { }
    }
$end = microtime(true) - $start;
echo round($end,4) . "s for 'is_int' with hits\n";

$start = microtime(true);
for($i = 0; $i < $n_times; $i++)
    {
    if ( is_int(strpos($haystack_miss,$needle)) )
        { }
    }
$end = microtime(true) - $start;
echo round($end,4) . "s for 'is_int' with misses\n";
l008com
  • 1,699
  • 2
  • 11
  • 20
1

Since strpos returns either integer or boolean false it's okay to use is_numeric.
The question is:
What is more idiomatic and autodocumented/self-descriptive, using is_numeric or comparing return value to boolean? IMO, comparing to boolean false is much more intuitive:

$string = 'new object';
$found = strpos($string,'new');

echo (is_numeric($found)) ? 'found' : 'not found';
echo "\n";
# much better
echo ($found !== false)   ? 'found' : 'not found';
echo "\n";

Also, strpos(...) !== false is overused because it's what PHP documentation suggests. Thus it has become conventional.

felipsmartins
  • 13,269
  • 4
  • 48
  • 56
0
  1. Using strpos in this context is for is greater speed over preg_match, using is_numeric on top of it is negating this speed advantage by adding more overhead.
  2. Using another function just to separate false from the rest makes no sense, better just use !== false

There is no reason why would anybody use is_numeric, but it will work, only slower.

About strpos vs preg_match: preg_match() vs strpos() for match finding? and which is the fast process strpos()/stripos() or preg_match() in php

Community
  • 1
  • 1
0

You are right, is_numeric works with strpos. But this can make the code tricky, hence lowering code readability.

Have in mind that although this may seems obvious to you, you are making another programmer that reads your code think in a lot of things:

  1. Does the haystack contains the needle?
  2. Which is the position of the needle in the haystack?
  3. What type of value does strpos returns?
  4. Is the returned value of strpos a number in this case?

And PHP can be quite a tricky language by itself, take a look at this examples:

if (strpos("needle in a haystack","needle")!==false) {
    echo "Needle found!<br>";
} else {
    echo "nothing found<br>";
}

if (is_numeric(strpos("needle in a haystack","needle"))) {
    echo "Needle found!<br>";
} else {
    echo "nothing found<br>";
}

if (is_int(strpos("needle in a haystack","needle"))) {
    echo "Needle found!<br>";
} else {
    echo "nothing found<br>";
}

// This doesn't work since 0 == false is true
if (strpos("needle in a haystack","needle")!=false) {
    echo "Needle found!<br>";
} else {
    echo "nothing found<br>";
}

// But this works since "haystack" position is not 0
if (strpos("needle in a haystack","haystack")!=false) {
    echo "Haystack found!<br>";
} else {
    echo "nothing found<br>";
}

// This doesn't work also because "needle" is at 0, and 0 is not a truthy value
if (strpos("needle in a haystack","needle")) {
    echo "Needle found!<br>";
} else {
    echo "nothing found<br>";
}

// But this works again since "haystack" position is not 0, and any int that's not 0 is truthy
if (strpos("needle in a haystack","haystack")) {
    echo "Haystack found!<br>";
} else {
    echo "nothing found<br>";
}

Imho, the best option is using ===false and ==!false comparisons, like explained in the php documentation for strpos:

Warning This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.

PD: For a better definition of "truthy" take a look at this post.

Community
  • 1
  • 1
marian0
  • 664
  • 6
  • 15