Hello list,
a quite common use case is that one needs to find out if a string
$haystack begins or ends with another string $needle.
Or in other words, if $needle is a prefix or a suffix of $haystack.

One prominent example would be in PSR-4 or PSR-0 class loaders.
Maybe the use case also occurs when writing parsers..
In each of these two examples (parsers, class loaders), we care about
performance.

(forgive me if this was discussed before, I did not find it anywhere
in the archives)

--------------------------

Existing solutions to this problem feel non-trivial, and/or are
suboptimal in performance.
https://stackoverflow.com/questions/2790899/how-to-check-if-a-string-starts-with-a-specified-string
https://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
This answer compares different solutions,
https://stackoverflow.com/a/7168986/246724

Existing solutions:
(Let's focus on string_starts_with(), the other case is mostly
equivalent / symmetric)

if (0 === strpos($haystack, $needle)) {..}
I have often seen this presented as the preferable solution.
Unfortunately, this searches the entire string, not just the
beginning. Especially if $haystack is really long, this can be a
waste.
E.g. if (0 === strpos(file_get_contents('some_source_file.php'),
'<?php')) {..} will search the entire file for an occurence of
'<?php'.

if ($needle === substr($haystack, 0, strlen($needle))) {..}
This reserves new memory for the substring, which later needs to be
garbage-collected.
Also, this requires an additional function call to strlen() - which
adds even more clutter if $needle is an expression, not just a
variable.

if (0 === strncmp($haystack, $needle, strlen($needle))) {..}
Needs the additional call to strlen().
Otherwise, this seems like a really good solution.

if ('' === $needle || false !== strrpos($haystack, $needle,
-strlen($haystack))) {..}
This is the funky solution from https://stackoverflow.com/a/10473026/246724
The author says that it will be outperformed by strncmp() - so..

if (preg_match('/^' . preg_quote($needle, '/') . '/', $haystack)) {..}
Clearly gonna be slower than other options.

As said, all these solutions do work, but they are either suboptimal,
or they add clutter and overhead, or feel a bit like mind acrobatics.

-----------------

So, I wonder if it would be worthwhile to add new functions
string_starts_with() / string_has_prefix(), and string_ends_with() /
string_has_suffix().

(Or maybe change strncmp(), so that the 3rd parameter $len is
optional. If $len is NULL / not provided, it would use the length of
the second (or first?) string.
(idea was that second parameter = needle).)

For me personally, I am sure that I would use a new
string_starts_with() a lot more often than a lot of the other existing
string functions.
I don't think it is an exotic or niche use case.

--------------

Spinning this further:
A lot of times if I want to check if $haystack begins with $needle, I
will then need the rest of the string after $needle.
So
if (string_starts_with($haystack, $needle)) {
    $suffix = substr($haystack, strlen($needle));
}
or
if (string_ends_with($filename, '.php')) {
    $basename = substr($filename, 0, -4);
}

I wonder if this could be somehow combined.
E.g.
if (FALSE !== $basename = string_clip_suffix($filename, '.php')) {
    // Do something with $basename.
}

------------------

One flaw of these new functions would be that they are less versatile
than other string functions.
They solve this problem, and nothing else.
On the other hand, this is the point, to avoid unnecessary overhead.

The other problem would be, of course, "feature creep" aka "we have so
many string functions already".
This is a matter of opinion.
I would imagine the "cost" of new native functions is:
- global namespace pollution
- increased mental load to learn and remember all of them
- higher memory footprint of php engine?
- more C code to maintain
- a new doc page.
Did I miss something?

------------------

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to