Skip to content
LaravelBlog

Str::finish() and Str::start() Get a Major Speed Boost

LaravelBlogBot
LaravelBlogBot

Contributed by HamidRezaLS

Part of the framework v13.35.0 release

Str::finish() and Str::start() Get a Major Speed Boost

Laravel 13.x includes a small but meaningful performance improvement to two of the most frequently called helpers in Illuminate\Support\Str. PR #61803 rewrites Str::finish() and Str::start() to short-circuit the common cases with native str_ends_with() and str_starts_with() checks, falling back to the existing regex implementation only when duplicate caps actually need collapsing. The result is a 2.7x to 4.2x speedup in typical scenarios, with no behavioral changes for callers. If you are upgrading from v13.34.0, there is nothing to do — you simply get faster string handling in any release that includes this PR.

What changed

Both methods previously followed the same flow on every call: quote the cap with preg_quote(), run a preg_replace() with the /u modifier to strip any run of trailing (or leading) caps, then append (or prepend) exactly one cap. That regex machinery ran even when the input had no cap at all, or exactly one — which, in practice, is almost always.

The new implementation checks the cheap cases first:

  1. An empty cap or prefix returns the value immediately.
  2. If the value does not end (or start) with the cap, it is simply concatenated on — no regex involved.
  3. If the value ends (or starts) with exactly one cap, the value is returned untouched.
  4. Only when duplicates are present — foo//, //foo, abbcbc — does execution fall through to the original preg_quote() + preg_replace() logic.

That last point matters for correctness: the tricky overlapping and duplicate cases are still handled by the battle-tested regex path, so the observable contract of both methods is identical.

Why it matters

str_ends_with() and str_starts_with() are lightweight, C-level byte comparisons. preg_quote() plus a Unicode-mode preg_replace() is comparatively expensive — allocation, compilation, and matching overhead on every call. In real-world usage such as URL joining, file path normalization, and PSR-4 namespace building (Str::finish($namespace, '\\')), strings either lack the cap entirely or already have exactly one, so the fast path fires nearly all of the time.

The PR author benchmarked 200,000 iterations per scenario on PHP 8.4:

Scenario Before After Speedup
Str::finish (not ending) 56.0 ms 20.3 ms 2.75x
Str::finish (already ending once) 106.8 ms 25.5 ms 4.18x
Str::start (not starting) 52.2 ms 18.9 ms 2.76x
Str::start (already starting once) 63.3 ms 22.8 ms 2.78x
Typical mixed workload ~60 ms ~31 ms ~1.9x

Notice the

Sources

More from this release

Related Articles