tcb13/substringy
Substringy is a small PHP utility for working with substrings through a clean, fluent API. It provides helpers to extract text before, after, or between delimiters, along with common string slicing and search operations, making everyday string manipulation simpler in Laravel and standalone PHP proje...
The package metadata doesn't point at a repository, so the first five minutes should be spent reading the installed source rather than guessing at the API.
composer require tcb13/substringy
composer show tcb13/substringy # version, deps, autoload namespace
ls vendor/tcb13/substringy/src # usually one or two small classes
Three things to confirm before writing any application code:
composer show will reveal either danielstjules/stringy (abandoned) or a maintained fork such as voku/stringy / statamic/stringy. That determines the base API you inherit and whether PHP 8.2+ will emit deprecations.tcb13/substringy is small enough that the whole public surface is a single file — read the method list once and you're done.A realistic first use case is pulling a field out of an unstructured line without hand-rolling preg_match offsets:
use Tcb13\Substringy\Substringy; // verify exact casing in src/ first
$line = 'GET /api/users/42?debug=1 HTTP/1.1';
$path = Substringy::create($line)
->between(' ', ' ') // '/api/users/42?debug=1'
->before('?'); // '/api/users/42'
Substringy::create($string) is the Stringy static factory and is the preferred entry point because it handles null and sets up UTF-8 correctly. new Substringy($string) works too, but you lose the null-coalescing.
Wrap at the boundary, chain in the middle. Instantiate once where the string enters your domain — a log line, a scraped href, a query value — then chain. Each Stringy call returns a new instance, so a chain is a single readable pipeline:
$user = Substringy::create($request->header('Referer'))
->afterLast('/')
->before('?')
->toLowerCase()
->toString();
Cast with ->toString() or (string) $s at the edge of your code so the rest of the app deals in plain strings, not Stringy objects.
Use it for delimiter-driven extraction, not for regex you already know. The package earns its place on the "I need the bit between these two markers" cases: between(), after(), before(), last-occurrence variants, and whatever extract()/cut()-style helpers the class exposes for capture groups. For anything with variable structure, a real preg_match with named groups will be clearer.
Laravel integration. Don't mix it with Illuminate\Support\Str — they're separate fluent APIs with overlapping method names and different semantics. Bridge at one point and be consistent:
// app/Support/Text.php
function text(?string $s): Substringy
{
return Substringy::create((string) $s, 'UTF-8');
}
Then use it inside parsers, Artisan import commands, and Blade-free presentation code. Typical wins: parsing access logs in a command, normalising imported CSV columns, slicing fixed-format report output, extracting IDs out of URLs in a redirect map.
Multi-byte by default. Stringy operates on characters, not bytes, when given an encoding. That's exactly what you want for user input and file names. Pass 'UTF-8' explicitly in the factory rather than relying on mb_internal_encoding(), so behaviour doesn't change between CLI and web requests.
Build a domain subclass. Because it's a Stringy subclass, you can extend it with your own helpers (toSlug, maskEmail, extractOrderId) and keep the fluent chaining intact:
final class Sku extends Substringy
{
public function family(): self
{
return $this->before('-'); // 'ACME-0042-BLK' -> 'ACME'
}
}
SubStringy vs Substringy in a use statement works on macOS/Windows and explodes in Linux CI. Copy the class name from the file, not from memory.''. Truthiness checks like if ($result) are unreliable; compare explicitly or guard with ->contains().null never survives the constructor. Stringy normalises null to ''. If downstream code distinguishes "absent" from "empty", you must track that flag yourself before wrapping.composer.json requires danielstjules/stringy, expect deprecation noise on PHP 8.1+ and a stalled dependency tree. Mitigate with a Composer alias/replacement to voku/stringy if the API matches, or vendor a copy. Check composer why-not php 8.3 early.var_dump($s) shows the object's internals; use var_dump($s->toString()) or dump((string) $s). Half the "the method returned garbage" reports are really "I looked at the wrong layer"./ when the subject is a path or URL, and add the u modifier for UTF-8 subjects.preg_match or native strpos/substr in the hot loop and keep Substringy for the readable outer layer.create() returns static, not self. If the inherited factory is hard-coded to self, YourSubclass::create($s) will hand you a base Substringy and your custom methods will error with "call to undefined method". Override create() in your subclass if so.composer.json and add your own test covering whichever methods you actually rely on.composer dump-autoload before filing a bug; PSR-4 mismatches in small packages are usually stale autoload files, not broken code.How can I help you explore Laravel packages today?