Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Matcher Laravel Package

loupe/matcher

Loupe Matcher turns search queries and arbitrary text into precise matches: tokenize phrases and negations, normalize locale-specific spelling variants, decompose compound words, calculate match spans, and format results with highlighting, cropping, and truncation.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Feature scoping: "search results that look like search results." Highlighting matched terms and cropping a contextual snippet is a table-stakes expectation for internal search, docs search, help-center search, and admin/CMS list views. This package lets you scope that as a small, discrete feature (one service, one Blade component) rather than an open-ended UX project.
  • Build vs. buy for presentation-layer search logic. The hard part of snippets is not the <mark> tag — it's word boundaries, diacritics (é vs e), quoted phrases, negated terms (-exclude), stop-word noise, and cropping on token boundaries instead of mid-word. That is several days of fiddly, edge-case-heavy work plus a permanent test-suite liability. Buying it as an MIT dependency is the cheap decision.
  • Search backend choice becomes decoupled. Because Formatter accepts pre-calculated matches, highlighting does not force your hand on the engine. You can ship with database LIKE/Scout today, migrate to Meilisearch/Elasticsearch later, and keep one consistent snippet renderer in the UI layer. This de-risks a roadmap decision we would otherwise delay until the search engine is "final."
  • Roadmap phasing.
    • Phase 1 — Single SearchHighlighter service + Blade component used on one high-traffic results page; measure click-through and query refinement rate.
    • Phase 2 — Roll out to admin/CMS/search-adjacent surfaces; tune cropLength, marker text, and highlight tags per surface via config, not code.
    • Phase 3 — Localization: ext-intl tokenization and ICU transliteration mean diacritic-insensitive matching comes mostly free in additional locales; validate per market before promising it. Compound-word languages (German, Dutch, Nordic) benefit from the decompounder, whose cost profile improved materially in 0.4.0 (see below).
  • Concrete use cases worth evaluating against your backlog:
    • Help desk / ticketing: surface the matched phrase inside a long ticket body so agents don't re-read the whole thread.
    • E-commerce / catalog: highlight matched attributes and crop long descriptions to the relevant fragment.
    • Docs & knowledge base: snippet-around-best-match instead of the first 160 characters (which is often boilerplate).
    • Compliance / legal review: flag and locate sensitive clauses in contract text — cheap first-pass triage before anything model-based.
    • Moderation: locate flagged terms in user-generated content for reviewer context.
  • What it does not decide for you: relevance ranking, indexing, typo tolerance, or faceting. Treat this strictly as the rendering layer of search. Buying it does not reduce the scope of the search-backend work.

When to Consider This Package

Consider adopting if:

  • You are on PHP/Laravel and displaying search results in HTML today or next quarter.
  • You want locale-aware, diacritic-normalized matching but do not want to own that logic.
  • Your results are plain text (or you can strip markup before formatting) — cropping is string-based, not DOM-aware.
  • You are comfortable pinning a version and wrapping the dependency behind a thin internal interface.
  • The alternative in your codebase is a hand-rolled str_ireplace/preg_replace highlighter, which almost certainly mis-highlights on word boundaries and diacritics.

Look elsewhere / hold off if:

  • Maturity is the blocker. This is still explicitly a work in progress with an unstable API, pre-1.0 (now 0.4.1), a small star count, and no dependents. There is no evidence of production usage at scale. The positive signal is cadence: 0.4.0 was a competent, benchmark-backed release, and 0.4.1 followed quickly as another performance-only patch — a team actively working the known weak spot rather than a maturity upgrade. Do not put it on a critical path without an adapter and a fallback.
  • You need client-side highlighting. Live filtering in the browser, collaborative editing, or reactive type-ahead belong in JS — pushing every keystroke through PHP is the wrong architecture.
  • Your source text is rich HTML/Markdown. Cropping a string can split tags or break markup; you would need to pre-sanitize or use a DOM-aware approach.
  • Volume/latency is tight. This is pure-PHP tokenization per result. Two releases have now chipped at this risk without eliminating it:
    • 0.4.0 redesigned the decompounder cache (substring cache → complete-token LRU), yielding 2.45× faster / ~59% less wall time and ~17% lower peak memory (20.6s → 8.4s, 83MB → 69MB) on the warmed bench-movies benchmark.
    • 0.4.1 ships a further highlighting speedup targeting long texts — directly relevant to our heaviest intended surfaces (ticket bodies, contract clauses, long descriptions). No published benchmark accompanies it yet, and it does not change the architecture: cost is still per-result, per-query.
    • Both data points are synthetic, warmed vendor benchmarks. Keep the plan to batch, cache formatted snippets keyed by (document, query), or lean on matches your search engine already returns.
  • You need phrase/negation semantics to drive the query itself. This library interprets phrases and negations for highlighting only; it is not a query parser for your search engine and will not make results respect them.
  • Hard dependency concerns: requires ext-intl; confirm it is present in your deployment images before committing. Also confirm PHP-version support against your fleet — check the 0.4.x composer.json rather than assuming continuity from 0.2.x.

Adoption gate I would set: pin an exact version in composer.json (0.4.1, not a range), wrap it in a single SnippetFormatter interface, add a snapshot test suite for our top 20 real queries, and re-run our own benchmark against our corpus and locales before scaling — the vendor benchmarks prove the trend, not our latency. Revisit at 1.0. If the API churns, we swap the implementation, not the call sites.

How to Pitch It (Stakeholders)

For executives (one paragraph):

Search results currently show raw text with no indication of why a result matched, and descriptions get truncated at arbitrary character counts. This MIT-licensed PHP library gives us Google-style snippets — matched terms highlighted, text cropped to the relevant context, correct across languages and accented characters — as a drop-in component. We can ship it in one to two sprints instead of building and permanently maintaining tokenization and cropping logic ourselves. The trade-off is honest: the library is pre-1.0 and explicitly marked "work in progress," so I recommend we isolate it behind our own interface, pin the version, and run it on one results page first to measure impact before wider rollout. The last two releases were both performance work — roughly 60% cheaper matching and ~17% lower memory in 0.4.0, plus a further speedup for long documents in 0.4.1 — which materially reduces the performance concern we flagged at intake, though we should confirm that on our own data before promising it. No additional licensing cost; the primary investment is engineering time plus a small ongoing upgrade tax.

For engineering (the ask):

loupe/matcher is a three-part pipeline: Tokenizer (ICU word boundaries, quoted phrases, negated terms, locale-aware) → Matcher (stop-word filtering, match spans) → Formatter (configurable highlight tags, crop length, crop/truncation markers, plus a standalone Cropper for already-highlighted text). Two API details make integration cheap: Formatter::format(..., matches: $existingMatches) lets us skip matching entirely when our search engine already returns hit positions, and FormatterOptions::fromArray() means all tuning (crop length, tags, markers) lives in config/ rather than code. Proposed work: bind it in a service provider, expose one SearchHighlighter contract with a null/plain-text fallback implementation, add snapshot tests over real queries, and feature-flag it on a single results page. New in 0.4.1: a performance patch that speeds up match highlighting on long texts (PR #39) — no API change, so it lands behind our existing interface with zero call-site work; bump the pin from 0.4.0 to 0.4.1 and re-run our snapshot suite. Carried over from 0.4.0: the decompounder cache redesign (substring cache → complete-token LRU), reported at 2.45× faster and 14.5 MB lower peak memory on the warmed bench-movies benchmark. Risks to track: pre-1.0 API churn (mitigated by the interface + version pin), ext-intl in our images, per-result CPU cost on large result sets (reduced twice now, still nonzero and not uniform across languages — validate with our own dataset; mitigate via snippet caching), and markup safety if source text is HTML.

The one-line decision framing: This is a low-cost, low-risk, high-visible-polish win — 0.4.0 improved the performance baseline and 0.4.1 extends that to the long-text cases we care about most, and neither changes anything about the integration plan — provided we treat it as a replaceable rendering dependency rather than a foundation.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table