hoa/ustring
Hoa\Ustring is a PHP library for handling Unicode strings as objects, offering UTF-8-aware manipulation of grapheme clusters, case conversion, search, replace, and iteration for safer, more robust multilingual text processing in PHP applications.
What it is. hoa/ustring is the Unicode string component of the Hoa project: an object-oriented Ustring class (ArrayAccess, Countable, IteratorAggregate) providing grapheme-cluster-aware charAt, substring, split, toLowerCase, padding, transliteration, and a Search helper. It pulls in hoa/consistency and hoa/exception, and depends on ext-mbstring/ext-iconv for encoding work.
Architecture fit. Poor for anything we would build today. The library predates and is superseded by two things already present in a Laravel codebase: Illuminate\Support\Str / Str::of() (a fluent Stringable API that mirrors the Ustring ergonomics most teams actually adopted it for) and symfony/string (AbstractString, UnicodeString, Slugger), which is a first-party Symfony component with active maintenance and native PHP 8 support. Adopting hoa/ustring would add a parallel, unmaintained string abstraction alongside two supported ones — a net increase in surface area for zero functional gain.
Integration feasibility. Mechanically possible in a legacy context, commercially unattractive in any context:
--ignore-platform-req=php means shipping code that has never been executed against our interpreter. Additionally, Hoa packages historically lean on Hoa\Consistency autoloading and hoa:// stream-wrapper conventions that sit awkwardly beside Composer 2 / PSR-4 expectations.mbstring/intl and with whatever the frontend or database collation does. That is a silent-correctness problem, not a crash — the worst kind.Technical risk summary
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Blocks PHP/Laravel upgrade | High | High | Do not adopt; contain and replace |
| Silent Unicode divergence (emoji, grapheme counts) | High | Medium–High if used for validation | Parity corpus + golden-file tests |
| No security patch channel | Certain | Medium | Internal fork, pinned version, audit review |
Abandonment of docs (hoa-project.net) |
Medium | Low | Mirror docs into internal wiki |
Hidden global state / serialization of Ustring objects |
Medium | Medium | Adapter boundary, never in queue payloads |
Key questions for the owning team.
hoa/ustring actually in composer.lock, and via which direct or transitive path? (composer why hoa/ustring.)mb_* plus Str satisfy the original requirement?Stack fit. Treat this as negative fit and design accordingly. The target state is: symfony/string (or Str/Stringable) for all new code, intl's grapheme_* functions where true grapheme segmentation is required, and hoa/ustring permitted only inside an explicitly quarantined adapter until deleted.
Migration path.
composer why hoa/ustring, then grep -rn "Hoa\\\\Ustring" across app/, packages/, and any legacy modules. Classify each call site as formatting, comparison, or validation.composer.json and add a conflict/CI guard (Deptrac, PHPStan, or a simple PHPCS forbiddenNames rule) that fails the build on any new Hoa\ reference. Add a composer prohibits php 8.0 hoa/ustring check to the upgrade-readiness dashboard.App\Support\Contracts\UnicodeString with length, substr, lower, upper, contains, slug — implemented first by a Hoa-backed adapter (preserving byte-for-byte current behaviour) so the wrapper is a no-op refactor and ships independently of the migration.symfony/string and flip the container binding. Legacy code and new code both consume the interface; only the binding changes.Compatibility notes.
acme/hoa-ustring) rather than relaxing platform requirements globally — this confines the "unsupported interpreter" risk to one repository we control and can patch.mbstring function-overload settings and default_charset are consistent between the legacy Hoa path and the new one; mismatches here produce subtle truncation bugs at boundary lengths.Effort sizing. For typical Laravel usage (slug generation, limit/words, a few charAt/substr calls) expect 2–5 engineer-days including parity tests. If the library sits inside a bulk text-processing or search-indexing pipeline, budget 10–15 days and plan a dual-run shadow period.
Maintenance. Total ownership transfer. There are no upstream releases, no security advisories, and no CVE feed we can subscribe to; every future PHP, PCRE, ICU, or mbstring behaviour change is ours to diagnose and patch. Budget a recurring dependency-audit line item and an annual "replace or re-fork" review. Because the repo is archived, issue reporting is not merely slow — it does not exist.
Support. No vendor, no commercial support, no active maintainer, no community channel. Escalation path is internal only. Documentation lives on a project site with no continuity commitment; mirror the relevant API pages into our wiki now while they are still reachable. Zero dependents also means no peer organisations to share fixes or migration notes with.
Scaling. Runtime cost is negligible — pure PHP, no network hop, no service dependency. The real scaling concern is allocation: the design favours an object per string, which is inappropriate inside hot loops over large collections. Also note that Ustring instances are not safe to serialize into queued jobs or cache payloads across deploys; keep the wrapper's public surface returning native strings.
Failure modes to design against.
mbstring defaults or missing iconv between local, CI, and production yield different results for the same input.Ramp-up. Low-to-negative. New engineers will not have seen this API, will not find current tutorials, and will be steered by every modern reference toward Str/symfony/string. Onboarding cost is therefore mostly rework cost — the wrapper contract is what keeps that bounded, since new hires can be told "use the interface, ignore the implementation" and never need to learn Hoa at all. Hiring signal is mildly negative if the dependency appears prominently in the stack.
Recommendation. Do not adopt for new work. Where it already exists, treat it as a contained legacy liability: wrap, replace with symfony/string/Str, and remove within one planning cycle — with the wrapper prioritised ahead of any PHP or Laravel upgrade.
How can I help you explore Laravel packages today?