PHPackages                             rebeccathedev/search-parser - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. rebeccathedev/search-parser

ActiveLibrary[Database &amp; ORM](/categories/database)

rebeccathedev/search-parser
===========================

A parser that converts a freeform query into an intermediate object, that can be converted to query many backends (SQL, ElasticSearch, etc).

v0.3(5mo ago)30MITPHPPHP &gt;=8.2CI passing

Since Nov 22Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/rebeccathedev/search-parser)[ Packagist](https://packagist.org/packages/rebeccathedev/search-parser)[ RSS](/packages/rebeccathedev-search-parser/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (1)Versions (4)Used By (0)

🔍 SearchParser
==============

[](#-searchparser)

A powerful search query parser that transforms freeform search queries into structured objects for querying SQL databases, Eloquent models, or other backends.

✨ Features
----------

[](#-features)

- 🎯 Parse Google-style search queries with field names, ranges, negation, and more
- 🗄️ Built-in transforms for SQL (PDO) and Laravel Eloquent
- 🔌 Extensible parser system for custom query types
- 🔒 Field filtering and mapping for security
- 🔎 Loose mode for fuzzy matching
- 🚀 PHP 8.2+ with modern type safety

🚀 Quick Example
---------------

[](#-quick-example)

```
use RebeccaTheDev\SearchParser\SearchParser;

$parser = new SearchParser();
$query = $parser->parse('from:foo@example.com "bar baz" !meef date:2018/01/01-2018/08/01');
```

This tokenizes the search into a `SearchQuery` object with structured components:

```
RebeccaTheDev\SearchParser\SearchQuery Object
(
    [position:RebeccaTheDev\SearchParser\SearchQuery:private] => 0
    [data:protected] => Array
        (
            [0] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => field
                    [field] => from
                    [value] => foo@example.com
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

            [1] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => bar baz
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

            [2] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => meef
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] => 1
                )

            [3] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => range
                    [field] => date
                    [value] =>
                    [firstRangeValue] => 2018/01/01
                    [secondRangeValue] => 2018/08/01
                    [negate] =>
                )

            [4] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => #hashtag
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

        )

)

```

📦 Installation
--------------

[](#-installation)

```
composer require rebeccathedev/search-parser
```

**Requirements:** PHP 8.2+

No external dependencies required for core functionality. Eloquent transform requires `illuminate/database`.

📖 Usage
-------

[](#-usage)

### Basic Parsing

[](#basic-parsing)

```
use RebeccaTheDev\SearchParser\SearchParser;

$parser = new SearchParser();
$query = $parser->parse('from:foo@example.com "exact phrase" !excluded');
```

The `SearchQuery` object is iterable:

```
foreach ($query as $component) {
    echo $component->type;  // 'field', 'text', 'range'
    echo $component->value;
}
```

### 🔧 Custom Parsers

[](#-custom-parsers)

Extend the parser by implementing the `Parser` interface:

```
use RebeccaTheDev\SearchParser\Parsers\Parser;
use RebeccaTheDev\SearchParser\SearchQueryComponent;

class HashtagParser implements Parser {
    public function parsePart(string $part): SearchQueryComponent {
        $component = new SearchQueryComponent();

        if (preg_match('!#(.*)!', $part, $match)) {
            $component->type = 'hashtag';
            $component->value = $match[1];
        }

        return $component;
    }
}

// Use it
$parser = new SearchParser();
$parser->addParser(new HashtagParser());
$query = $parser->parse('search #trending');
```

See `src/Parsers/Hashtag.php` for a working example. Note: parsers don't fall through - if your parser handles a part, processing moves to the next part.

🔄 Transforms
------------

[](#-transforms)

Transform parsed queries into SQL WHERE clauses or Eloquent query builders.

### 💾 SQL Transform

[](#-sql-transform)

```
use RebeccaTheDev\SearchParser\Transforms\SQL\SQL;

$pdo = new PDO("sqlite:/tmp/database.db");
$transform = new SQL('default_field', $pdo);

$query = $parser->parse('from:foo@example.com "bar baz" !meef date:2018/01/01-2018/08/01');
$where = $transform->transform($query);

// Result:
// `from` = 'foo@example.com' and `default_field` = 'bar baz' and
// `default_field` != 'meef' and (`date` between '2018/01/01' and '2018/08/01')
```

### ✨ Eloquent Transform

[](#-eloquent-transform)

```
use RebeccaTheDev\SearchParser\Transforms\Eloquent\Eloquent;

$users = User::query();
$transform = new Eloquent('name', $users);

$query = $parser->parse('status:active age:25-35');
$users = $transform->transform($query)->get();
```

### 🔎 Loose Mode

[](#-loose-mode)

Enable fuzzy matching with `LIKE` queries:

```
$transform = new SQL('default_field', $pdo);
$transform->looseMode = true;
$where = $transform->transform($query);

// Result:
// `from` = 'foo@example.com' and `default_field` like '%bar baz%' and
// `default_field` not like '%meef%' and (`date` between '2018/01/01' and '2018/08/01')
```

### 🎨 Custom Component Transforms

[](#-custom-component-transforms)

Add custom transforms for your custom parsers:

```
use RebeccaTheDev\SearchParser\Transforms\SQL\Hashtag;

$pdo = new PDO("sqlite:/tmp/database.db");
$transform = new SQL('default_field', $pdo);
$transform->addComponentTransform(new Hashtag('default_field', $pdo));

$query = $parser->parse('search #trending');
$where = $transform->transform($query);

// Result: `default_field` = 'search' and `hashtag` = 'trending'
```

See `src/Transforms/SQL/Hashtag.php` for a working example.

🛡️ Filters
----------

[](#️-filters)

### ⚠️ Security Note

[](#️-security-note)

**Important:** The SQL transform escapes *values* but not *field names*. Always allowlist allowed fields before passing queries to transforms. Never trust user input for field names.

### 🎯 FieldFilter

[](#-fieldfilter)

Allowlist allowed fields for security:

```
use RebeccaTheDev\SearchParser\Filters\{Filter, FieldFilter};

$filter = new Filter();
$fieldFilter = new FieldFilter();
$fieldFilter->validFields = ['from', 'to', 'subject', 'date'];
$filter->addFilter($fieldFilter);

$query = $parser->parse('from:foo@example.com invalid:malicious subject:test');
$filtered = $filter->filter($query);

// Only 'from' and 'subject' fields are kept, 'invalid' is removed
```

### 🗺️ FieldNameMapper

[](#️-fieldnamemapper)

Map user-facing field names to database column names:

```
use RebeccaTheDev\SearchParser\Filters\{Filter, FieldNameMapper};

$filter = new Filter();
$mapper = new FieldNameMapper();
$mapper->mappingFields = [
    'date' => 'created_at',
    'author' => 'user_id'
];
$filter->addFilter($mapper);

$query = $parser->parse('date:2024-01-01-2024-12-31 author:123');
$filtered = $filter->filter($query);

// 'date' becomes 'created_at', 'author' becomes 'user_id'
```

### ⚙️ Custom Filters

[](#️-custom-filters)

Implement the `FiltersQueries` interface:

```
use RebeccaTheDev\SearchParser\Filters\FiltersQueries;
use RebeccaTheDev\SearchParser\SearchQuery;

class MyCustomFilter implements FiltersQueries {
    public function filter(SearchQuery $query): SearchQuery {
        foreach ($query as $component) {
            // Your custom filtering logic
        }
        return $query;
    }
}

$filter = new Filter();
$filter->addFilter(new MyCustomFilter());
```

Useful `SearchQuery` methods:

- `remove(SearchQueryComponent $item)` - Remove a component
- `replace(SearchQueryComponent $old, SearchQueryComponent $new)` - Replace a component
- `merge(SearchQuery $query)` - Merge two queries

🧪 Testing
---------

[](#-testing)

```
composer install
./vendor/bin/phpunit
```

Some tests may be skipped if optional dependencies (like Eloquent) aren't installed.

📄 License
---------

[](#-license)

MIT License - see LICENSE file for details.

👩‍💻 Author
----------

[](#‍-author)

Made with 🩷 by [Rebecca Peck](https://github.com/rebeccathedev)

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance72

Regular maintenance activity

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1289 days

Total

3

Last Release

156d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e44d774aeb52f48a966c8da491231abf0f0cf1fc9dfecc16266528aafd96b6db?d=identicon)[rebeccathedev](/maintainers/rebeccathedev)

---

Top Contributors

[![rebeccathedev](https://avatars.githubusercontent.com/u/500950?v=4)](https://github.com/rebeccathedev "rebeccathedev (14 commits)")

---

Tags

laravelparsingphpsearchsql

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/rebeccathedev-search-parser/health.svg)

```
[![Health](https://phpackages.com/badges/rebeccathedev-search-parser/health.svg)](https://phpackages.com/packages/rebeccathedev-search-parser)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k285.3M6.2k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k115.1M102](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[ramsey/uuid-doctrine

Use ramsey/uuid as a Doctrine field type.

90440.3M211](/packages/ramsey-uuid-doctrine)[reliese/laravel

Reliese Components for Laravel Framework code generation.

1.7k3.4M16](/packages/reliese-laravel)[wildside/userstamps

Laravel Userstamps provides an Eloquent trait which automatically maintains `created\_by` and `updated\_by` columns on your model, populated by the currently authenticated user in your application.

7511.7M13](/packages/wildside-userstamps)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
