307

I want to get the first letter of a string and I've noticed that $str[0] works great. I am just not sure whether this is 'good practice', as that notation is generally used with arrays. This feature doesn't seem to be very well documented so I'm turning to you guys to tell me if it's all right – in all respects – to use this notation?

Or should I just stick to the good ol' substr($str, 0, 1)?

Also, I noted that curly braces ($str{0}) works as well. What's up with that?

Tatu Ulmanen
  • 123,288
  • 34
  • 187
  • 185

9 Answers9

423

Yes. Strings can be seen as character arrays, and the way to access a position of an array is to use the [] operator. Usually there's no problem at all in using $str[0] (and I'm pretty sure is much faster than the substr() method).

There is only one caveat with both methods: they will get the first byte, rather than the first character. This is important if you're using multibyte encodings (such as UTF-8). If you want to support that, use mb_substr(). Arguably, you should always assume multibyte input these days, so this is the best option, but it will be slightly slower.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Hock
  • 5,784
  • 2
  • 19
  • 25
  • 7
    Does PHP $str[0] take into account that there can be 2Byte long chars? UTF and such? (even though substr() does not help with it either!) – Tomer W Jun 26 '13 at 13:29
  • 82
    If you want to be extra super safe, you should go with `mb_substr($str, 0, 1, 'utf-8')` so you don't truncate a multibyte string. – Vic Aug 28 '13 at 02:22
  • 2
    Link to the docs: http://www.php.net/manual/en/language.types.string.php#language.types.string.substr – biziclop Sep 17 '13 at 17:57
  • 18
    Although this is shorter and is easier to remember than ```substr($str, 0, 1)```, this confuses who reads the code. – trante Nov 02 '13 at 10:13
  • 10
    The choice between square-brackets and substr() is largely a matter of preference, but be aware that the result is different when applied to an empty string. If $s = "" then $s[] === "", but substr($s, 0, 1) === false. – xtempore Jul 06 '14 at 02:02
  • 10
    If $s = "" then $s[0] will generate a "Notice: Uninitialized string offset: 0" whereas substr($s, 0, 1) will not. – chris Oct 28 '14 at 12:58
  • I have a problem with such char Arrays. As we can access $str[0], but why we can't access foreach($str as $c)... . – Abhisek Malakar Dec 06 '14 at 11:02
  • 2
    -1; while vague enough that it's not *outright* false, saying that *"Strings can be seen as Char Arrays"* is pretty misleading; they have pretty much nothing in common besides being indexable with `[0]`- or `{0}`-style indexes. Array functions don't work on strings (nor vica-versa), and the special syntax for appending to arrays (`$some_array[] = $new_element`) doesn't work on strings. – Mark Amery Mar 12 '17 at 14:09
  • Any proof of concept and citation for ***Strings can be seen as character arrays***? I don't find array functions working for `String`!! **EDIT:** just noticed that @MarkAmery already pointed that out. However, a citation is still needed. – Fr0zenFyr Oct 10 '17 at 06:44
  • Can we use array_shift($string) to get the first char? – Odin Thunder Dec 05 '17 at 14:29
  • No, we can`t :) – Odin Thunder Dec 05 '17 at 14:35
52

The {} syntax is deprecated as of PHP 5.3.0. Square brackets are recommended.

Kelderic
  • 6,502
  • 8
  • 46
  • 85
Mara Morton
  • 4,429
  • 1
  • 21
  • 12
  • 14
    http://docs.php.net/language.types.string : `Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose. However, this syntax is deprecated as of PHP 5.3.0. Use square brackets instead, such as $str[42].` – VolkerK Dec 29 '09 at 00:24
  • 4
    @VolkerK: at the link you provided I noticed they removed the note on the PHP manual they left only: `Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose.` So I'm wondering if they decided that using `{}` is NOT deprecated anymore as of PHP 6 – Marco Demaio Feb 21 '12 at 18:58
  • 1
    @MarcoDemaio The link now tells the what MichaelMorton says. – Tino Feb 20 '14 at 22:27
  • 1
    "gives no indication of deprecation" - Indeed, the deprecation message has been removed in revision 304518 - `The curly-brackets-string-index-accessor-syntax does not emit any deprecation notice, although the original notice have been on and off for PHP 5.x, it does not in the current version, thrus we should not label it as deprecated. Related to bug #52254` - https://svn.php.net/repository/phpdoc/en/trunk/language/types/string.xml?p=304518 – VolkerK Mar 15 '17 at 12:40
  • As of today (10th May'18), a quote from the liked [PHP docs](http://docs.php.net/language.types.string#language.types.string.substr): `Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose. ` Seems like this syntax is going to stay for a while. – Fr0zenFyr May 10 '18 at 09:43
  • And it's been updated again. It's now deprecated as of PHP 7.4.0 and removed in 8.0.0. – RobinHood70 May 14 '21 at 21:49
30

Lets say you just want the first char from a part of $_POST, lets call it 'type'. And that $_POST['type'] is currently 'Control'. If in this case if you use $_POST['type'][0], or substr($_POST['type'], 0, 1)you will get C back.

However, if the client side were to modify the data they send you, from type to type[] for example, and then send 'Control' and 'Test' as the data for this array, $_POST['type'][0] will now return Control rather than C whereas substr($_POST['type'], 0, 1) will simply just fail.

So yes, there may be a problem with using $str[0], but that depends on the surrounding circumstance.

gattsbr
  • 3,762
  • 1
  • 22
  • 34
  • 3
    As a side note to circumvent this particular issue and in either case one should always perform data validation. `if (true === is_string($_POST['type']))` – Will B. Aug 12 '15 at 07:49
13

My only doubt would be how applicable this technique would be on multi-byte strings, but if that's not a consideration, then I suspect you're covered. (If in doubt, mb_substr() seems an obviously safe choice.)

However, from a big picture perspective, I have to wonder how often you need to access the 'n'th character in a string for this to be a key consideration.

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
John Parker
  • 54,048
  • 11
  • 129
  • 129
12

In case of multibyte (unicode) strings using str[0] can cause a trouble. mb_substr() is a better solution. For example:

$first_char = mb_substr($title, 0, 1);

Some details here: Get first character of UTF-8 string

Community
  • 1
  • 1
Sergey Burish
  • 584
  • 6
  • 8
9

It'll vary depending on resources, but you could run the script bellow and see for yourself ;)

<?php
$tests = 100000;

for ($i = 0; $i < $tests; $i++)
{
    $string = md5(rand());
    $position = rand(0, 31);

    $start1 = microtime(true);
    $char1 = $string[$position];
    $end1 = microtime(true);
    $time1[$i] = $end1 - $start1;

    $start2 = microtime(true);
    $char2 = substr($string, $position, 1);
    $end2 = microtime(true);
    $time2[$i] = $end2 - $start2;

    $start3 = microtime(true);
    $char3 = $string{$position};
    $end3 = microtime(true);
    $time3[$i] = $end3 - $start3;
}

$avg1 = array_sum($time1) / $tests;
echo 'the average float microtime using "array[]" is '. $avg1 . PHP_EOL;

$avg2 = array_sum($time2) / $tests;
echo 'the average float microtime using "substr()" is '. $avg2 . PHP_EOL;

$avg3 = array_sum($time3) / $tests;
echo 'the average float microtime using "array{}" is '. $avg3 . PHP_EOL;
?>

Some reference numbers (on an old CoreDuo machine)

$ php 1.php 
the average float microtime using "array[]" is 1.914701461792E-6
the average float microtime using "substr()" is 2.2536706924438E-6
the average float microtime using "array{}" is 1.821768283844E-6

$ php 1.php 
the average float microtime using "array[]" is 1.7251944541931E-6
the average float microtime using "substr()" is 2.0931363105774E-6
the average float microtime using "array{}" is 1.7225742340088E-6

$ php 1.php 
the average float microtime using "array[]" is 1.7293763160706E-6
the average float microtime using "substr()" is 2.1037721633911E-6
the average float microtime using "array{}" is 1.7249774932861E-6

It seems that using the [] or {} operators is more or less the same.

Willy Stadnick
  • 151
  • 1
  • 6
  • 2
    Nice test! Some numbers from a 3 year old Xeon: the average float microtime using "array[]" is 2.2427082061768E-7 the average float microtime using "substr()" is 3.9647579193115E-7 the average float microtime using "array{}" is 2.1522283554077E-7 – Ellert van Koperen Jun 02 '16 at 06:50
  • for accurate measurements, you should imho better do microtime out of the loop, and don't mix the different approaches within the same loop. – PypeBros Aug 04 '16 at 09:35
  • @PypeBros can you elaborate about the advantages of your approach and perhaps share the code? thanks – Willy Stadnick Aug 07 '16 at 19:06
  • assuming that `microtime()` measures with precision p, that is, `microtime() - p < real time < microtime+p`, doing `before=mt(); 1000_testsA; after=mt()` allows you to compute execution of 1 iteration with precision p/1000. – PypeBros Aug 09 '16 at 08:59
  • 1
    not mixing execution of `testA` and `testB` within the same loops means you are capable of detecting e.g. the fact that `testB` is a cache-killer while `testA` is cache-friendly. When they're both in the same loop, they are measured to have the very same timings because `testB` polluted `testA`'s caching. – PypeBros Aug 09 '16 at 09:01
  • 1
    similarly, I'd avoid generating strings or randoms within the testing loops and have them ready in an array nearby. – PypeBros Aug 09 '16 at 09:03
  • 1
    -1; leaving aside the questionable timing mechanism (it would be better to time many operations than to time them one at a time; I worried upon reading this that just the time taken doing the `microtime()` call would make up most of the time difference, although experimentally that seems to not be true), there's no reason to care about the tiny speed difference here. It's a fraction of a millionth of a second; when is this *ever* going to matter? – Mark Amery Mar 12 '17 at 14:38
9
$str = 'abcdef';
echo $str[0];                 // a
Jakir Hossain
  • 2,457
  • 18
  • 23
  • 6
    -1; the OP's question was whether this syntax was a bad practice, and you've responded by... repeating the syntax, without any commentary? This is not an answer. – Mark Amery Mar 12 '17 at 14:32
7

Speaking as a mere mortal, I would stick with $str[0]. As far as I'm concerned, it's quicker to grasp the meaning of $str[0] at a glance than substr($str, 0, 1). This probably boils down to a matter of preference.

As far as performance goes, well, profile profile profile. :) Or you could peer into the PHP source code...

Stephen
  • 3,515
  • 5
  • 27
  • 33
1

I've used that notation before as well, with no ill side effects and no misunderstandings. It makes sense -- a string is just an array of characters, after all.

Kaleb Brasee
  • 51,193
  • 8
  • 108
  • 113
  • No, a string is not an array of characters (at least as PHP uses those two terms). -1. – Mark Amery Mar 12 '17 at 14:40
  • @MarkAmery [Characters within strings may be accessed and modified by specifying the zero-based offset of the desired character after the string using square array brackets, as in `$str[42]`. Think of a string as an array of characters for this purpose.](http://php.net/manual/en/language.types.string.php#language.types.string.substr) Internally, PHP strings are byte arrays. – gattsbr Mar 13 '17 at 18:59
  • @gattsbr internally they are, but as far as the model that PHP exposes goes, they're a fundamentally different thing. Accessing an offset using square bracket notation is pretty much the only operation that they have in common with arrays; string functions don't work on arrays, nor vica versa, and the array append syntax (`$arr[] = $new_element`) doesn't work on strings. As such, I don't think conceiving of strings as character arrays is useful. – Mark Amery Mar 13 '17 at 19:03
  • @markamery better get on rewriting the php.net manual then to include such a minuscule technicality. – gattsbr Mar 13 '17 at 19:14