revolution/laravel-str-mixins
Adds custom macro mixins to Laravel's Str helper, extending it with handy string methods you can call fluently anywhere. A lightweight way to register reusable string macros across your app without repeating helper code.
Install and verify registration before writing any application code.
composer require revolution/laravel-str-mixins
php artisan package:discover
php artisan optimize:clear
The package registers its closures against Illuminate\Support\Str (which is Macroable) from a service provider. Check vendor/revolution/laravel-str-mixins/composer.json for extra.laravel.providers. If it's listed, discovery handles everything. If not — common in micro-packages — add the provider to bootstrap/providers.php (Laravel 11+) or config/app.php (Laravel 10 and below) manually.
Where to look first, in order:
vendor/revolution/laravel-str-mixins/src/ — the mixin class is the documentation. Every public function on it becomes a Str macro; the README will be thinner than the source. Read it end to end. At this size there is no excuse not to.composer.json — check the illuminate/support constraint against your pinned Laravel version. Small packages lag on major-version constraints even when the code is functionally compatible.tinker, not prose:// php artisan tinker
Str::hasMacro('yourMethodName'); // true once registered
array_keys((new ReflectionClass(Str::class))->getMethods());
First use case. The realistic value is repeated low-value text chores: deriving display labels, building initials for avatars, normalizing user input, trimming/cleaning identifiers, extracting segments. Pick one live call site, not a greenfield prototype:
use Illuminate\Support\Str;
$initials = Str::of($user->name)->squish()->explode(' ')->map(fn ($p) => Str::substr($p, 0, 1))->implode('');
Confirm the package method you planned to use actually exists and behaves as you expect on empty strings, multibyte input, and null before you spread it across the codebase.
Wrap the sanctioned subset in an internal class. This is the single highest-leverage move. It converts an unvetted external dependency into an internal implementation detail, and makes the eventual vendoring decision a non-event:
namespace App\Support;
use Illuminate\Support\Str;
final class Text
{
public static function initials(?string $name): string
{
return Str::of((string) $name)->squish()
->explode(' ')
->filter()
->map(fn ($part) => Str::substr($part, 0, 1))
->take(2)
->implode('');
}
}
Application code calls Text::initials(), never the package directly. If you drop the dependency, you edit one file.
Chain through Str::of() / Stringable pipelines at boundaries. The mixins compose naturally with core methods, so use them in form request preparation, API resource formatting, and Eloquent accessors — places where a fluent pipeline reads better than three nested preg_replace calls:
protected function slug(): Attribute
{
return Attribute::make(
get: fn () => Str::of($this->title)->squish()->slug()->toString(),
);
}
Register a Stringable macro when the transform is only used mid-chain. Str::macro() gives you a static call; Stringable::macro() gives you ->yourThing() and keeps pipelines fluent. Mixin registration on Str does not automatically give you the Stringable form in every case, so pick deliberately based on how you'll call it.
Contract tests per helper you consume. One test per method, asserting output for: normal input, empty string, null, multibyte/Unicode, and leading/trailing whitespace. These are what catch both silent macro collisions and framework shadowing on upgrade:
it('produces initials', function () {
expect(Text::initials('Jane van Doe'))->toBe('JD');
expect(Text::initials(''))->toBe('');
});
Add @method stubs in the same PR as adoption. PHPStan/Larastan and IDEs cannot resolve macros; without stubs you get CI false positives and IDE warnings, and the adoption stalls because developers blame the package. A small stub file or a larastan baseline entry is cheaper than the friction. Do not defer this.
Pilot in one non-critical service, then review after one Laravel minor upgrade. If, at review, the helpers serve only one or two call sites, close the pilot and inline the logic. The cost/benefit flips fast on micro-dependencies.
Macroable::macro() overwrites without warning. A collision with another package or your own bootstrapping code is last-writer-wins — behavior changes, no exception. Detect with a boot-time assertion that captures the macro before/after registration, or simply rely on per-helper contract tests.Str/Stringable; audit the method inventory against your current version before adopting, not just at install.composer update churn, and put "re-validate string mixins" on the framework upgrade checklist.str_*/preg_* without grapheme-cluster or i18n handling. If you need correctness guarantees around Unicode or localization, use symfony/string (already in the Laravel stack) or core Str instead.revolution/laravel-str-mixins vs invokable/laravel-str-mixins). Minor, but it's friction for auditability and SBOM clarity when someone later asks "where does this come from?"php artisan about lists discovered providers), then Str::hasMacro() in Tinker. If it exists but returns the wrong result, suspect a collision — dump the closure with (new ReflectionMethod(Str::class, 'methodName'))->getClosure() and compare against the vendor source.How can I help you explore Laravel packages today?