168 lines
5.8 KiB
PHP
168 lines
5.8 KiB
PHP
<?php namespace utils;
|
|
|
|
/**
|
|
* Copyright 2015 OpenStack Foundation
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
**/
|
|
final class FilterParser
|
|
{
|
|
/**
|
|
* @param mixed $filters
|
|
* @param array $allowed_fields
|
|
* @throws FilterParserException
|
|
* @return Filter
|
|
*/
|
|
public static function parse($filters, $allowed_fields = array())
|
|
{
|
|
$res = [];
|
|
$matches = [];
|
|
|
|
if (!is_array($filters))
|
|
$filters = array($filters);
|
|
|
|
foreach ($filters as $filter) // parse AND filters
|
|
{
|
|
|
|
$f = null;
|
|
// parse OR filters
|
|
$or_filters = preg_split("|(?<!\\\),|", $filter);
|
|
|
|
if (count($or_filters) > 1) {
|
|
$f = [];
|
|
foreach ($or_filters as $of) {
|
|
|
|
//single filter
|
|
if(empty($of)) continue;
|
|
|
|
list($field, $op, $value) = self::filterExpresion($of);
|
|
|
|
if (!isset($allowed_fields[$field])){
|
|
throw new FilterParserException(sprintf("filter by field %s is not allowed", $field));
|
|
}
|
|
if (!in_array($op, $allowed_fields[$field])){
|
|
throw new FilterParserException(sprintf("%s op is not allowed for filter by field %s",$op, $field));
|
|
}
|
|
// check if value has AND or OR values on same field
|
|
$same_field_op = null;
|
|
if(str_contains($value, '&&')){
|
|
$values = explode('&&', $value);
|
|
if (count($values) > 1) {
|
|
$value = $values;
|
|
$same_field_op = 'AND';
|
|
}
|
|
}
|
|
else if(str_contains($value, '||')){
|
|
$values = explode('||', $value);
|
|
if (count($values) > 1) {
|
|
$value = $values;
|
|
$same_field_op = 'OR';
|
|
}
|
|
}
|
|
|
|
$f_or = self::buildFilter($field, $op, $value, $same_field_op);
|
|
if (!is_null($f_or))
|
|
$f[] = $f_or;
|
|
}
|
|
} else {
|
|
//single filter
|
|
|
|
list($field, $op, $value) = self::filterExpresion($filter);
|
|
|
|
// check if value has AND or OR values on same field
|
|
$same_field_op = null;
|
|
if(str_contains($value, '&&')){
|
|
$values = explode('&&', $value);
|
|
if (count($values) > 1) {
|
|
$value = $values;
|
|
$same_field_op = 'AND';
|
|
}
|
|
}
|
|
else if(str_contains($value, '||')){
|
|
$values = explode('||', $value);
|
|
if (count($values) > 1) {
|
|
$value = $values;
|
|
$same_field_op = 'OR';
|
|
}
|
|
}
|
|
|
|
if (!isset($allowed_fields[$field])){
|
|
throw new FilterParserException(sprintf("filter by field %s is not allowed", $field));
|
|
}
|
|
if (!in_array($op, $allowed_fields[$field])){
|
|
throw new FilterParserException(sprintf("%s op is not allowed for filter by field %s",$op, $field));
|
|
|
|
}
|
|
|
|
$f = self::buildFilter($field, $op, $value, $same_field_op);
|
|
}
|
|
|
|
if (!is_null($f))
|
|
$res[] = $f;
|
|
}
|
|
return new Filter($res);
|
|
}
|
|
|
|
/**
|
|
* @param string $exp
|
|
* @return array
|
|
* @throws FilterParserException
|
|
*/
|
|
public static function filterExpresion(string $exp){
|
|
|
|
preg_match('/[=<>][=>@]{0,1}/', $exp, $matches);
|
|
|
|
if (count($matches) != 1)
|
|
throw new FilterParserException(sprintf("invalid OR filter format %s (should be [:FIELD_NAME:OPERAND:VALUE])", $exp));
|
|
|
|
$op = $matches[0];
|
|
$operands = explode($op, $exp, 2);
|
|
$field = $operands[0];
|
|
$value = $operands[1];
|
|
|
|
return [$field, $op, $value];
|
|
}
|
|
/**
|
|
* Factory Method
|
|
*
|
|
* @param string $field
|
|
* @param string $op
|
|
* @param mixed $value
|
|
* @param string $same_field_op
|
|
* @return FilterElement|null
|
|
*/
|
|
public static function buildFilter($field, $op, $value, $same_field_op = null )
|
|
{
|
|
switch ($op) {
|
|
case '==':
|
|
return FilterElement::makeEqual($field, $value, $same_field_op);
|
|
break;
|
|
case '=@':
|
|
return FilterElement::makeLike($field, $value, $same_field_op);
|
|
break;
|
|
case '>':
|
|
return FilterElement::makeGreather($field, $value, $same_field_op);
|
|
break;
|
|
case '>=':
|
|
return FilterElement::makeGreatherOrEqual($field, $value, $same_field_op);
|
|
break;
|
|
case '<':
|
|
return FilterElement::makeLower($field, $value, $same_field_op);
|
|
break;
|
|
case '<=':
|
|
return FilterElement::makeLowerOrEqual($field, $value, $same_field_op);
|
|
break;
|
|
case '<>':
|
|
return FilterElement::makeNotEqual($field, $value, $same_field_op);
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
} |