krak / aql
API 查询语言
Requires
- krak/lex: ^0.2.0
Requires (Dev)
- peridot-php/peridot: ^1.18
- symfony/var-dumper: ^3.2
README
API 查询语言是一个库,用于验证/转换字符串表达式,以便在 API 中用于查询/过滤数据。它允许使用用户生成的表达式来查询数据集,同时允许完全验证以防止任何不希望的表达式类型。
安装
使用 composer 在 krak/aql
中安装。
用法
以下示例可能是理解这个库如何工作的最简单方法。
<?php use Krak\AQL; $engine = AQL\Engine::createWithDomain([ 'orders' => ['status', 'created_at'] ]); $query = 'orders.created_at < date(now()) and orders.status = "cancelled"'; try { $processed_query = $engine->process($query); } catch (AQL\AQLException $e) { // any errors regarding data syntax or semantics will be caught here. }
AQL\Engine::process
执行多项操作。
- 它将表达式解析为抽象语法树 (AST)。任何语法或词法错误都会在这里抛出。这包括无效字符或表达式。
- 它对生成的 AST 进行语义分析,以验证表达式是否合理并强制执行任何自定义领域规则。在上面的示例中,它确保比较的字段位于
orders.{status,created_at}
领域内。 - 它运行任何自定义转换
- 然后,它将 AST 编译回字符串,用于生成查询。
最终的 $processed_query
已经过验证,可以用作 SQL WHERE 子句的一部分或类似的内容。因此,它为 API 提供了一种支持强大查询界面的方式,同时提供了对生成的查询的完全控制。
排序查询
除了可以用于数据库 WHERE 子句的表达式查询外,该库还支持解析排序表达式,这些表达式可能出现在数据库 ORDER BY 子句中。
<?php use Krak\AQL; $engine = AQL\Engine::createSortWithDomain([ 'categories' => ['sort', 'created_at'] ]); $query = 'categories.sort DESC, categories.created_at'; // defaults to ASCENDING. try { $processed_query = $engine->process($query); } catch (AQL\AQLException $e) { // any errors regarding data syntax or semantics will be caught here. }
访问者
访问者提供了一种转换 AST 的方法。在引擎的上下文中,访问者在语义分析之后运行。每个访问者实现 Krak\AQL\AST\Visitor
。AST 接受访问者然后以 深度优先前序遍历 的方式遍历自身。
链访问者
链访问者 Krak\AQL\AST\ChainVisitor
接受一个其他访问者的数组。当 AST 被遍历时,它会委托给每个其他访问者。这允许在一遍中完成许多转换。
DoubleToSingleQuotes 访问者
这个访问者 Krak\AQL\Visitor\DoubleToSingleQuotesVisitor
将双引号字符串转换为单引号。
因此,这个输入
"string"
将被映射到
'string'
重命名标识符访问者
这个访问者 Krak\AQL\Visitor\RenameIdVisitor
重命名标识符。您可以使用点符号来访问子标识符。
$ast->accept(new AQL\Visitor\RenameIdVisitor([ 'a' => 'alpha', 'b' => 'beta', 'b.a' => 'attribute', ]));
使用这个重命名访问者,它会应用以下转换
a = b.a
变为
alpha = beta.attribute
FuncEval 访问者
FuncEval 允许您评估一个函数并将其转换为另一个元素值。
它可以将以下输入
1 = id(1)
转换为
1 = 1
查看 example/func-eval.php 以查看一个工作示例。
语义分析
语义分析提供了对解析的 AST 的额外验证,以确保解析的表达式在语义上是正确的。
SA 执法者只是简单地会在 AST 失败语义分析时抛出 SAException
的访问者。SA 通过单遍完成,因为它使用了 AST\ChainVisitor
。
要利用 SA,您创建您的执法者列表并构建 SemanticAnalysis
对象。
<?php $sa = new SA\SemanticAnalysis([$enforce]); $sa->analyze($ast);
强制领域
强制标识符位于某个领域内。
<?php $enforce = new SA\EnforceDomain([ 'user' => [ 'id', 'email', 'group' => ['id', 'name'] ] ]);
这允许使用任何标识符路径,例如 user.id
、user.email
、user.group.id
以及其他几个。
EnforceFunc
仅强制使用某些函数。
<?php $enforce = new SA\EnforceFunc(['now', 'date']);
这将仅允许使用 now
和 date
函数,其他任何内容都会抛出异常。
Parser
AQLParser 有两个接口:ExpressionParser 和 SortParser。
ExpressionParser 会假设字符串输入是一个表达式并进行相应的解析。SortParser 会假设字符串是一个排序表达式列表并进行相应的解析。每个解析器将根据情况返回一个根 AST 节点,即表达式或排序表达式列表。
运算符
从高到低优先级
()
< = > <= >= IN LIKE
AND
OR
EBNF(语法)
Expression ::= AndExpression | AndExpression "OR" Expression
AndExpression ::= OpExpression | OpExression "AND" AndExpression
OpExpression ::= Element
OpExpression ::= Element "<" OpExpression
OpExpression ::= Element ">" OpExpression
OpExpression ::= Element "=" OpExpression
OpExpression ::= Element "!=" OpExpression
OpExpression ::= Element "<=" OpExpression
OpExpression ::= Element ">=" OpExpression
OpExpression ::= Element "LIKE" OpExpression
OpExpression ::= Element "IN" "(" ValueList ")"
Element ::= Value | IdExpression | Func | "(" Expression ")"
Value ::= string | number
ValueList ::= Value | Value "," ValueList
IdExpression ::= identifier | identifier "." IdExpression
Func ::= identifier "(" ElementList ")"
ElementList ::= Element | Element "," ElementList
SortExpressionList ::= SortExpression | SortExpression "," SortExpressionList
SortExpression ::= IdExpression "DESC"
SortExpression ::= IdExpression "ASC"
SortExpression ::= IdExpression
string = "[^"]\*"
number = (\d*\.\d+|\d+)
identifier = [_a-zA-Z][_a-zA-Z0-9]*
测试
测试通过 Peridot 执行。
make test