krak/aql

API 查询语言

v0.2.2 2017-04-09 08:47 UTC

This package is auto-updated.

Last update: 2024-09-18 17:58:16 UTC


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 执行多项操作。

  1. 它将表达式解析为抽象语法树 (AST)。任何语法或词法错误都会在这里抛出。这包括无效字符或表达式。
  2. 它对生成的 AST 进行语义分析,以验证表达式是否合理并强制执行任何自定义领域规则。在上面的示例中,它确保比较的字段位于 orders.{status,created_at} 领域内。
  3. 它运行任何自定义转换
  4. 然后,它将 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.iduser.emailuser.group.id 以及其他几个。

EnforceFunc

仅强制使用某些函数。

<?php

$enforce = new SA\EnforceFunc(['now', 'date']);

这将仅允许使用 nowdate 函数,其他任何内容都会抛出异常。

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