Str::of

Wraps a string in a Stringable object so string helpers can be chained fluently instead of nested.

Signature
Str::of(string $string): \Illuminate\Support\Stringable

Parameters

ParameterTypeDescription
$$stringstringThe value to wrap. It is stored as-is; no transformation happens here.

Return value

Illuminate\Support\StringableA new Stringable instance wrapping the given string.

Str::of is the entry point to Laravel's fluent string API. It returns an Illuminate\Support\Stringable whose methods mirror the static Str methods but take the subject implicitly and return a new Stringable, so transformations read left to right instead of inside-out.

When it is worth it

One call is not worth wrapping — Str::lower($v) beats Str::of($v)->lower()->toString(). Three or more is where it pays, especially when the chain includes conditionals: when(), unless(), whenEmpty() and pipe() let you branch mid-chain without breaking it into temporary variables.

The object is not a string

This is what trips people up. A Stringable renders correctly with echo, in string interpolation and in Blade, and it compares equal with ==. But === against a string is always false, is_string() returns false, in_array($chain, $list, true) misses, and a function declared under strict_types=1 with a string parameter will reject it outright. End the chain with ->toString() or a (string) cast whenever the value leaves your code.

The global str() helper is the same thing with less typing.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Clean a pasted value: trim it, collapse whitespace, lowercase it and strip punctuation in one pass.
  • Build a slug or filename from several conditional transformations without temporary variables.
  • Parse a delimited string with explode/after/before while keeping the pipeline readable.

Frequently asked questions

What is the difference between Str::of() and the str() helper?

Nothing, for a string argument — str($value) simply calls Str::of($value) and returns the same Stringable. Use str() in application code and Str::of() in a package where you would rather not depend on the global helper being loaded.

How do I convert a Stringable back to a string?

Call ->toString() or cast with (string). Both are required before strict comparison, is_string() checks, or passing the value into a function declared under strict_types=1.

Why does my comparison against Str::of() always return false?

Because === compares an object to a string and can never match. Either end the chain with ->toString(), or use the Stringable's own predicate methods such as ->is(), ->exactly() or ->startsWith(), which return real booleans.

More examples

Conditional steps without breaking the chain

squish collapses the internal runs of whitespace as well as trimming the ends. when() runs its callback only on the second call, so both branches share one pipeline instead of duplicating it behind an if statement.

Example · php
<?php

use Illuminate\Support\Str;

function reference(string $raw, bool $anonymise): string
{
    return Str::of($raw)
        ->squish()
        ->upper()
        ->when($anonymise, fn ($s) => $s->mask('*', 2, -2))
        ->prepend('REF-')
        ->toString();
}

echo reference('  ab 12 cd 34 ', false), "\n";
// REF-AB 12 CD 34

echo reference('  ab 12 cd 34 ', true), "\n";
// REF-AB*******34

The str() helper and Blade

Inside Blade the cast is unnecessary, which is why the object nature of Stringable so rarely bites in views and so often bites in a service class. Note startsWith returns a real bool, not a Stringable — predicate methods break the chain deliberately.

Example · php
<?php

// str() is the global equivalent of Str::of().
$slug = str($post->title)->slug()->toString();

// In Blade, echoing a Stringable works without the cast,
// because {{ }} runs the value through e() which calls __toString().
?>
<h1>{{ str($post->title)->title() }}</h1>

<p>{{ str($post->body)->stripTags()->squish()->limit(200) }}</p>

@if (str($post->slug)->startsWith('draft-'))
    <span>Draft</span>
@endif

Where the object identity bites

The first check silently fails a permission test rather than erroring, which is the expensive version of this bug. json_encode does the right thing because Stringable implements JsonSerializable, so serialisation hides the problem until something uses a strict comparison.

Example · php
<?php

use Illuminate\Support\Str;

$role = Str::of('  Admin ')->trim()->lower();

$allowed = ['admin', 'editor'];

var_dump(in_array($role, $allowed, true));
// bool(false)  — strict comparison against an object

var_dump(in_array($role, $allowed));
// bool(true)   — loose comparison casts via __toString()

var_dump(in_array($role->toString(), $allowed, true));
// bool(true)   — the version you should write

var_dump(json_encode(['role' => $role]));
// string(16) "{"role":"admin"}"

Discussion

  • Be the first to comment on this lesson.