technically/search-query
Composer 安装命令:
composer require technically/search-query
包简介
Parse plaintext search queries into easy-to-use structures
README 文档
README
🔍 Parse plaintext search queries into easy-to-use filter structures.
This library takes a human-typed search query string and parses it into a structured Query object containing typed filters (KeywordFilter, FieldFilter). It supports quoted strings, negation, comparison operators, and field-based filtering.
Installation
composer require technically/search-query
Requirements:
- PHP 8.4+
Quick Start
use Technically\SearchQuery\QueryParser; $parser = new QueryParser(); $query = $parser->parse('tag:php -legacy "best practices"'); foreach ($query->filters as $filter) { // Filter instances... }
Supported Query Syntax
| Syntax | Parsed As |
|---|---|
hello |
KeywordFilter('hello') |
"hello world" |
KeywordFilter('hello world', quoted: true) |
-hello |
KeywordFilter('hello', exclude: true) |
tag:php |
FieldFilter('tag', ':', 'php') |
-tag:php |
FieldFilter('tag', ':', 'php', exclude: true) |
year>2020 |
FieldFilter('year', '>', '2020') |
year>=2020 |
FieldFilter('year', '>=', '2020') |
year<2020 |
FieldFilter('year', '<', '2020') |
year<=2020 |
FieldFilter('year', '<=', '2020') |
hello\ world |
KeywordFilter('hello world') (escaped whitespace) |
"custom field":value |
FieldFilter('custom field', ':', 'value') |
Negation
A leading - (minus) before a keyword or field filter negates it. Multiple minuses are gracefully collapsed into a single negation.
-apple -> KeywordFilter('apple', exclude: true)
-tag:legacy -> FieldFilter('tag', ':', 'legacy', exclude: true)
Quoting
Double quotes group multiple words into a single token. Quotes can be escaped with \.
"hello world" -> KeywordFilter('hello world', quoted: true)
field:"hello world" -> FieldFilter('field', ':', 'hello world', quoted: true)
Escaping
The backslash \ escape character works both inside and outside quoted strings:
apples\ fruits -> KeywordFilter('apples fruits')
55\" -> KeywordFilter('55"')
"hello \"world\"" -> KeywordFilter('hello "world"', quoted: true)
The Tolerant Reader
The parser is built using the Tolerant Reader design pattern — to be forgiving with malformed input. It never throws.
API Reference
QueryParser
The main entry point for parsing query strings.
use Technically\SearchQuery\QueryParser; $parser = new QueryParser(); $query = $parser->parse('your search query');
The parser accepts an optional Tokenizer instance in its constructor. By default, it uses QueryTokenizer.
Methods
parse(string $query): Query— Parses a query string into aQueryobject.
Query
An immutable value object representing the parsed search query.
use Technically\SearchQuery\Query; $query = new Query([ new KeywordFilter('php'), new FieldFilter('tag', ':', 'tutorial'), ]);
Properties
public readonly array $filters— Array ofFilterinstances.
Methods
static empty(): self— Create a new empty query.isEmpty(): bool— Check if the query is empty (has no filters).toString(): string— Serializes the query back to the search query syntax string.
Filters
All filters implement the Technically\SearchQuery\Filters\Filter marker interface.
KeywordFilter
Represents a free-text keyword search term.
use Technically\SearchQuery\Filters\KeywordFilter; new KeywordFilter('php'); new KeywordFilter('hello world', quoted: true); new KeywordFilter('legacy', exclude: true);
Properties:
public readonly string $keyword— The keyword value.public readonly bool $quoted— Whether the keyword was originally quoted.public readonly bool $exclude— Whether the keyword is negated.
Methods:
toString(): string— Serializes the filter back to query syntax.
FieldFilter
Represents a field-based filter (field:operator:value).
use Technically\SearchQuery\Filters\FieldFilter; new FieldFilter('year', '>', '2020'); new FieldFilter('status', ':', 'active', quoted: true); new FieldFilter('tag', ':', 'legacy', exclude: true);
Properties:
public readonly string $field— The field name.public readonly FilterOperator $operator— The comparison operator.public readonly string $value— The filter value.public readonly bool $quoted— Whether the value was originally quoted.public readonly bool $exclude— Whether the filter is negated.
Methods:
toString(): string— Serializes the filter back to query syntax.
Examples
Parse a complex query
use Technically\SearchQuery\QueryParser; use Technically\SearchQuery\Filters\KeywordFilter; use Technically\SearchQuery\Filters\FieldFilter; $parser = new QueryParser(); $query = $parser->parse('php -legacy "best practices" year>=2020'); foreach ($query->filters as $filter) { if ($filter instanceof KeywordFilter) { echo "Keyword: {$filter->keyword}" . ($filter->exclude ? ' (excluded)' : '') . ($filter->quoted ? ' (quoted)' : '') . "\n"; } elseif ($filter instanceof FieldFilter) { echo "Field: {$filter->field} {$filter->operator->value} {$filter->value}" . ($filter->exclude ? ' (excluded)' : '') . ($filter->quoted ? ' (quoted)' : '') . "\n"; } } // Output: // Keyword: php // Keyword: legacy (excluded) // Keyword: best practices (quoted) // Field: year >= 2020
Serialize filters back to strings
$filter = new FieldFilter('tag', ':', 'hello world', quoted: true, exclude: true); echo $filter->toString(); // -tag:"hello world" // Or serialize an entire Query back to string: $query = new Query([ new KeywordFilter('php'), new FieldFilter('year', '>', '2020', exclude: true), ]); echo $query->toString(); // php -year>2020
Custom tokenization
use Technically\SearchQuery\QueryParser; use Technically\SearchQuery\Contracts\Tokenizer; class MyCustomTokenizer implements Tokenizer { public function tokenize(string $query): iterable { // Custom tokenization logic... } } $parser = new QueryParser(new MyCustomTokenizer());
Running Tests
composer tests
Tests are written with Pest PHP.
License
MIT
Credits
Implemented by 👾 Ivan Voskoboinyk.
统计信息
- 总下载量: 1
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-15