tags:

views:

378

answers:

6

Example: I have a $variable = "_foo", and I want to make absolutely sure that $variable does not start with an underscore "_". How can I do that in PHP? Is there some access to the char array behind the string?

+20  A: 
$variable[0] != "_"

How does it work?

In PHP you can get particular character of a string with array index notation. $variable[0] is the first character of a string (if $variable is a string).

pinusnegra
An explanation as to why this works would be awesome, but +1 anyway.
musicfreak
There you go. Added explanation for you.
Imran
You should test if the string is at least one byte long before accessing the first byte.
Gumbo
even better answer than the one chosen as the final.
dusoft
skipping the test works for me on PHP 5.3.0
Carson Myers
Amazing how people will think of substr and strpos BEFORE thinking of this...
luiscubal
amazing how people will ask for explanation instead of consulting the manual.
just somebody
@Carson Myers: Accessing non-existing bytes causes an *uninitialized string offset* notice.
Gumbo
This works for a single character, but i thought `substr` was a more generic solution that would work for multiple characters and I thought regex would be a step too far. That's not too say I didn't think of this... It's programming 101.
Alex Sexton
Python already has this kind of syntax for accessing sub-strings since long ago.
ghostdog74
+3  A: 

You might check out the substr function in php and grab the first character that way:

http://php.net/manual/en/function.substr.php

if (substr('_abcdef', 0, 1) === '_') { ... }
Alex Sexton
+2  A: 
function starts_with($s, $prefix){
    // returns a bool
    return strpos($s, $prefix) === 0;
}

starts_with($variable, "_");
The MYYN
+1 All other solutions barf on empty string input.
Asaph
Inefficient - scans the whole string if the prefix is not found immediately.
Seva Alekseyev
From the `substr` manual: "If string is less than or equal to start characters long, FALSE will be returned." So substr($foo,0,1) works perfectly with empty strings.
Wim
As Seva said, this is really too inefficient. If I had to use a function I would go with substr instead of strpos
AntonioCS
@Wim: Thanks for pointing that out. Yet another example of PHP featuring an unintuitive, yet convenient behavior.
Asaph
`if($s==="") return false;else use_other_solution();`That means other solutions can be made empty string-proof too.
luiscubal
A: 

Here’s a better starts with function:

function mb_startsWith($str, $prefix, $encoding=null) {
    if (is_null($encoding)) $encoding = mb_internal_encoding();
    return mb_substr($str, 0, mb_strlen($prefix, $encoding), $encoding) === $prefix;
}
Gumbo
what problem does this piece of overengineering solve?
just somebody
he's trying to check for underscores, not accents.
Carson Myers
@just somebody: It allows to test for arbitrary prefixes while taking multibyte strings into account.
Gumbo
A: 

To build on pinusnegra's answer, and in response to Gumbo's comment on that answer:

function has_leading_underscore($string) {

    return ($string[0] === '_') ? 'yes' : 'no';

}

Running on PHP 5.3.0, the following works and returns the expected value, even without checking if the string is at least 1 character in length:

echo has_leading_underscore('_somestring').', ';
echo has_leading_underscore('somestring').', ';
echo has_leading_underscore('').', ';
echo has_leading_underscore(null).', ';
echo has_leading_underscore(false).', ';
echo has_leading_underscore(0).', ';
echo has_leading_underscore(array('_foo', 'bar'));

/*
 * output: yes, no, no, no, no, no, no
 */

I don't know how other versions of PHP will react, but if they all work, then this method is probably more efficient than the substr route.

Carson Myers
+10  A: 

Since someone mentioned efficiency, I've benchmarked the functions given so far out of curiosity:

function startsWith1($str, $char) {
    return strpos($str, $char) === 0;
}
function startsWith2($str, $char) {
    return stripos($str, $char) === 0;
}
function startsWith3($str, $char) {
    return substr($str, 0, 1) === $char;
}
function startsWith4($str, $char){
    return $str[0] === $char;
}
function startsWith5($str, $char){
    return (bool) preg_match('/^' . $char . '/', $str);
}
function startsWith6($str, $char) {
    if (is_null($encoding)) $encoding = mb_internal_encoding();
    return mb_substr($str, 0, mb_strlen($char, $encoding), $encoding) === $char;
}

Here are the results on my average DualCore machine with 100.000 runs each

// Testing '_string'
startsWith1 took 0.385906934738
startsWith2 took 0.457293987274
startsWith3 took 0.412894964218
startsWith4 took 0.366240024567 <-- fastest
startsWith5 took 0.642996072769
startsWith6 took 1.39859509468

// Tested "string"
startsWith1 took 0.384965896606
startsWith2 took 0.445554971695
startsWith3 took 0.42377281189
startsWith4 took 0.373164176941 <-- fastest
startsWith5 took 0.630424022675
startsWith6 took 1.40699005127

// Tested 1000 char random string [a-z0-9]
startsWith1 took 0.430691003799
startsWith2 took 4.447286129
startsWith3 took 0.413349866867
startsWith4 took 0.368592977524 <-- fastest
startsWith5 took 0.627470016479
startsWith6 took 1.40957403183

// Tested 1000 char random string [a-z0-9] with '_' prefix
startsWith1 took 0.384054899216
startsWith2 took 4.41522812843
startsWith3 took 0.408898115158
startsWith4 took 0.363884925842 <-- fastest
startsWith5 took 0.638479948044
startsWith6 took 1.41304707527

As you can see, treating the haystack as array to find out the char at the first position is always the fastest solution. It is also always performing at equal speed, regardless of string length. Using strpos is faster than substr for short strings but slower for long strings, when the string does not start with the prefix. The difference is irrelevant though. stripos is incredibly slow with long strings. preg_match performs mostly the same regardless of string length, but is only mediocre in speed. The mb_substr solution performs worst, while probably being more reliable though.

Given that these numbers are for 100.000 runs, it should be obvious that we are talking about 0.0000x seconds per call. Picking one over the other for efficiency is a worthless micro-optimization, unless your app is doing startsWith checking for a living.

Gordon
Good work! (need extra chars to place small comment)
AntonioCS
+1 for the benchmarks
The MYYN