You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
902 lines
24 KiB
902 lines
24 KiB
1 year ago
|
<?php
|
||
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
**/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Trigger expressions parser.
|
||
|
*/
|
||
|
class C10TriggerExpression {
|
||
|
// For parsing of trigger expression.
|
||
|
const STATE_AFTER_OPEN_BRACE = 1;
|
||
|
const STATE_AFTER_BINARY_OPERATOR = 2;
|
||
|
const STATE_AFTER_LOGICAL_OPERATOR = 3;
|
||
|
const STATE_AFTER_NOT_OPERATOR = 4;
|
||
|
const STATE_AFTER_MINUS_OPERATOR = 5;
|
||
|
const STATE_AFTER_CLOSE_BRACE = 6;
|
||
|
const STATE_AFTER_CONSTANT = 7;
|
||
|
|
||
|
// Error type constants.
|
||
|
const ERROR_LEVEL = 1;
|
||
|
const ERROR_UNEXPECTED_ENDING = 2;
|
||
|
const ERROR_UNPARSED_CONTENT = 3;
|
||
|
|
||
|
/**
|
||
|
* Shows a validity of trigger expression
|
||
|
*
|
||
|
* @var bool
|
||
|
*/
|
||
|
public $isValid;
|
||
|
|
||
|
/**
|
||
|
* An error message if trigger expression is not valid
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
public $error;
|
||
|
|
||
|
/**
|
||
|
* Type of parsing error, on of self::ERROR_* constant or 0 when no errors.
|
||
|
*
|
||
|
* @var int
|
||
|
*/
|
||
|
public $error_type;
|
||
|
|
||
|
/**
|
||
|
* In case of error contain failed position in expression string. Contain -1 when no errors.
|
||
|
*
|
||
|
* @var int
|
||
|
*/
|
||
|
public $error_pos;
|
||
|
|
||
|
/**
|
||
|
* An array of trigger functions like {Zabbix server:agent.ping.last(0)}
|
||
|
* The array isn't unique. Same functions can repeat.
|
||
|
*
|
||
|
* @deprecated use result tokens instead
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
public $expressions = [];
|
||
|
|
||
|
/**
|
||
|
* An options array.
|
||
|
*
|
||
|
* Supported options:
|
||
|
* 'lldmacros' => true Enable low-level discovery macros usage in trigger expression.
|
||
|
* 'allow_func_only' => true Allow trigger expression without host:key pair, i.e. {func(param)}.
|
||
|
* 'collapsed_expression' => true Short trigger expression.
|
||
|
* For example: {439} > {$MAX_THRESHOLD} or {439} < {$MIN_THRESHOLD}
|
||
|
* 'calculated' => false Parse calculated item formula instead of trigger expression.
|
||
|
* 'host_macro' Array of macro supported as host name part in function.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
public $options = [
|
||
|
'lldmacros' => true,
|
||
|
'allow_func_only' => false,
|
||
|
'collapsed_expression' => false,
|
||
|
'calculated' => false,
|
||
|
'host_macro' => []
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Source string.
|
||
|
*
|
||
|
* @var
|
||
|
*/
|
||
|
public $expression;
|
||
|
|
||
|
/**
|
||
|
* Object containing the results of parsing.
|
||
|
*
|
||
|
* @var C10TriggerExprParserResult
|
||
|
*/
|
||
|
public $result;
|
||
|
|
||
|
/**
|
||
|
* Current cursor position.
|
||
|
*
|
||
|
* @var
|
||
|
*/
|
||
|
protected $pos;
|
||
|
|
||
|
/**
|
||
|
* Parser for binary operators.
|
||
|
*
|
||
|
* @var CSetParser
|
||
|
*/
|
||
|
protected $binaryOperatorParser;
|
||
|
|
||
|
/**
|
||
|
* Parser for logical operators.
|
||
|
*
|
||
|
* @var CSetParser
|
||
|
*/
|
||
|
protected $logicalOperatorParser;
|
||
|
|
||
|
/**
|
||
|
* Parser for the "not" operator.
|
||
|
*
|
||
|
* @var CSetParser
|
||
|
*/
|
||
|
protected $notOperatorParser;
|
||
|
|
||
|
/**
|
||
|
* Parser for the {TRIGGER.VALUE} macro.
|
||
|
*
|
||
|
* @var CMacroParser
|
||
|
*/
|
||
|
protected $macro_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for function macros.
|
||
|
*
|
||
|
* @var C10FunctionMacroParser
|
||
|
*/
|
||
|
protected $function_macro_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for function id macros.
|
||
|
*
|
||
|
* @var CFunctionIdParser
|
||
|
*/
|
||
|
protected $functionid_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for trigger functions.
|
||
|
*
|
||
|
* @var C10FunctionParser
|
||
|
*/
|
||
|
protected $function_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for LLD macros.
|
||
|
*
|
||
|
* @var CLLDMacroParser
|
||
|
*/
|
||
|
protected $lld_macro_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for LLD macros with functions.
|
||
|
*
|
||
|
* @var CLLDMacroFunctionParser
|
||
|
*/
|
||
|
protected $lld_macro_function_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for user macros.
|
||
|
*
|
||
|
* @var CUserMacroParser
|
||
|
*/
|
||
|
protected $user_macro_parser;
|
||
|
|
||
|
/**
|
||
|
* Parser for numbers with optional time or byte suffix.
|
||
|
*
|
||
|
* @var CNumberParser
|
||
|
*/
|
||
|
protected $number_parser;
|
||
|
|
||
|
/**
|
||
|
* Chars that should be treated as spaces.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $spaceChars = [' ' => true, "\r" => true, "\n" => true, "\t" => true];
|
||
|
|
||
|
/**
|
||
|
* @param array $options
|
||
|
* @param bool $options['lldmacros']
|
||
|
* @param bool $options['allow_func_only']
|
||
|
* @param bool $options['collapsed_expression']
|
||
|
* @param bool $options['calculated']
|
||
|
* @param bool $options['host_macro']
|
||
|
*/
|
||
|
public function __construct(array $options = []) {
|
||
|
$this->options = $options + $this->options;
|
||
|
|
||
|
$this->binaryOperatorParser = new CSetParser(['<', '>', '<=', '>=', '+', '-', '/', '*', '=', '<>']);
|
||
|
$this->logicalOperatorParser = new CSetParser(['and', 'or']);
|
||
|
$this->notOperatorParser = new CSetParser(['not']);
|
||
|
$this->macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
|
||
|
if ($this->options['collapsed_expression']) {
|
||
|
$this->functionid_parser = new CFunctionIdParser();
|
||
|
}
|
||
|
else {
|
||
|
$this->function_macro_parser = new C10FunctionMacroParser(['host_macro' => $this->options['host_macro']]);
|
||
|
}
|
||
|
$this->function_parser = new C10FunctionParser();
|
||
|
$this->lld_macro_parser = new CLLDMacroParser();
|
||
|
$this->lld_macro_function_parser = new CLLDMacroFunctionParser;
|
||
|
$this->user_macro_parser = new CUserMacroParser();
|
||
|
$this->number_parser = new CNumberParser([
|
||
|
'with_minus' => false,
|
||
|
'with_size_suffix' => true,
|
||
|
'with_time_suffix' => true
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse a trigger expression and set public variables $this->isValid, $this->error, $this->expressions,
|
||
|
* $this->macros
|
||
|
*
|
||
|
* Examples:
|
||
|
* expression:
|
||
|
* {Zabbix server:agent.ping.last(0)}=1 and {TRIGGER.VALUE}={$TRIGGER.VALUE}
|
||
|
* results:
|
||
|
* $this->isValid : true
|
||
|
* $this->error : ''
|
||
|
* $this->expressions : array(
|
||
|
* 0 => array(
|
||
|
* 'expression' => '{Zabbix server:agent.ping.last(0)}',
|
||
|
* 'pos' => 0,
|
||
|
* 'host' => 'Zabbix server',
|
||
|
* 'item' => 'agent.ping',
|
||
|
* 'function' => 'last(0)',
|
||
|
* 'functionName' => 'last',
|
||
|
* 'functionParam' => '0',
|
||
|
* 'functionParamList' => array (0 => '0')
|
||
|
* )
|
||
|
* )
|
||
|
*
|
||
|
* @param string $expression
|
||
|
*
|
||
|
* @return C10TriggerExprParserResult|bool returns a result object if a match has been found or false otherwise
|
||
|
*/
|
||
|
public function parse($expression) {
|
||
|
// initializing local variables
|
||
|
$this->result = new C10TriggerExprParserResult();
|
||
|
$this->isValid = true;
|
||
|
$this->error = '';
|
||
|
$this->error_type = 0;
|
||
|
$this->error_pos = -1;
|
||
|
$this->expressions = [];
|
||
|
|
||
|
$this->pos = 0;
|
||
|
$this->expression = $expression;
|
||
|
|
||
|
if ($this->options['collapsed_expression'] && $this->options['allow_func_only']) {
|
||
|
$this->isValid = false;
|
||
|
$this->error = 'Incompatible options.';
|
||
|
}
|
||
|
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$afterSpace = false;
|
||
|
$level = 0;
|
||
|
|
||
|
while (isset($this->expression[$this->pos])) {
|
||
|
$char = $this->expression[$this->pos];
|
||
|
|
||
|
if (isset($this->spaceChars[$char])) {
|
||
|
$afterSpace = true;
|
||
|
$this->pos++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch ($state) {
|
||
|
case self::STATE_AFTER_OPEN_BRACE:
|
||
|
switch ($char) {
|
||
|
case '-':
|
||
|
$state = self::STATE_AFTER_MINUS_OPERATOR;
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case '(':
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$level++;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->parseUsing($this->notOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_NOT_OPERATOR;
|
||
|
}
|
||
|
elseif ($this->parseConstant()) {
|
||
|
$state = self::STATE_AFTER_CONSTANT;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_BINARY_OPERATOR:
|
||
|
switch ($char) {
|
||
|
case '-':
|
||
|
$state = self::STATE_AFTER_MINUS_OPERATOR;
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case '(':
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$level++;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->parseConstant()) {
|
||
|
$state = self::STATE_AFTER_CONSTANT;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
|
||
|
if ($this->parseUsing($this->notOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_NOT_OPERATOR;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_LOGICAL_OPERATOR:
|
||
|
switch ($char) {
|
||
|
case '-':
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$state = self::STATE_AFTER_MINUS_OPERATOR;
|
||
|
break;
|
||
|
|
||
|
case '(':
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$level++;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
|
||
|
if ($this->parseUsing($this->notOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_NOT_OPERATOR;
|
||
|
}
|
||
|
elseif ($this->parseConstant()) {
|
||
|
$state = self::STATE_AFTER_CONSTANT;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_CLOSE_BRACE:
|
||
|
switch ($char) {
|
||
|
case ')':
|
||
|
if ($level == 0) {
|
||
|
break 3;
|
||
|
}
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$level--;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->parseUsing($this->binaryOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_BINARY_OPERATOR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ($this->parseUsing($this->logicalOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_LOGICAL_OPERATOR;
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_CONSTANT:
|
||
|
switch ($char) {
|
||
|
case ')':
|
||
|
if ($level == 0) {
|
||
|
break 3;
|
||
|
}
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$level--;
|
||
|
$state = self::STATE_AFTER_CLOSE_BRACE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->parseUsing($this->binaryOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_BINARY_OPERATOR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
|
||
|
if ($this->parseUsing($this->logicalOperatorParser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
|
||
|
$state = self::STATE_AFTER_LOGICAL_OPERATOR;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_NOT_OPERATOR:
|
||
|
switch ($char) {
|
||
|
case '-':
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$state = self::STATE_AFTER_MINUS_OPERATOR;
|
||
|
break;
|
||
|
|
||
|
case '(':
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$level++;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if (!$afterSpace) {
|
||
|
break 3;
|
||
|
}
|
||
|
|
||
|
if ($this->parseConstant()) {
|
||
|
$state = self::STATE_AFTER_CONSTANT;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case self::STATE_AFTER_MINUS_OPERATOR:
|
||
|
switch ($char) {
|
||
|
case '(':
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
|
||
|
$char, $this->pos, 1
|
||
|
);
|
||
|
$state = self::STATE_AFTER_OPEN_BRACE;
|
||
|
$level++;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->parseConstant()) {
|
||
|
$state = self::STATE_AFTER_CONSTANT;
|
||
|
}
|
||
|
else {
|
||
|
break 3;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$afterSpace = false;
|
||
|
$this->pos++;
|
||
|
}
|
||
|
|
||
|
if ($this->pos == 0) {
|
||
|
$this->error = $this->options['calculated']
|
||
|
? _('incorrect calculated item formula')
|
||
|
: _('Incorrect trigger expression.');
|
||
|
$this->isValid = false;
|
||
|
}
|
||
|
|
||
|
$errors = array_filter([
|
||
|
($level != 0) ? self::ERROR_LEVEL : 0,
|
||
|
($state != self::STATE_AFTER_CLOSE_BRACE && $state != self::STATE_AFTER_CONSTANT)
|
||
|
? self::ERROR_UNEXPECTED_ENDING : 0,
|
||
|
isset($this->expression[$this->pos]) ? self::ERROR_UNPARSED_CONTENT : 0
|
||
|
]);
|
||
|
$error = reset($errors);
|
||
|
|
||
|
if ($error) {
|
||
|
$exp_part = substr($this->expression, ($this->pos == 0) ? 0 : $this->pos - 1);
|
||
|
$this->error = $this->options['calculated']
|
||
|
? _s('incorrect calculated item formula starting from "%1$s"', $exp_part)
|
||
|
: _('Incorrect trigger expression.').' '._s('Check expression part starting from "%1$s".', $exp_part);
|
||
|
$this->error_type = $error;
|
||
|
$this->error_pos = $this->pos;
|
||
|
$this->isValid = false;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$this->result->source = $expression;
|
||
|
$this->result->match = $expression;
|
||
|
$this->result->pos = 0;
|
||
|
$this->result->length = $this->pos;
|
||
|
|
||
|
return $this->result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a list of the unique hosts, used in a parsed trigger expression or empty array if expression is not valid
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getHosts() {
|
||
|
if (!$this->isValid) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
return array_unique(zbx_objectValues($this->expressions, 'host'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse the string using the given parser. If a match has been found, move the cursor to the last symbol of the
|
||
|
* matched string.
|
||
|
*
|
||
|
* @param CParser $parser
|
||
|
* @param int $tokenType
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
protected function parseUsing(CParser $parser, $tokenType) {
|
||
|
if ($parser->parse($this->expression, $this->pos) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$this->result->addToken($tokenType, $parser->getMatch(), $this->pos, $parser->getLength());
|
||
|
$this->pos += $parser->getLength() - 1;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a constant in the trigger expression and moves a current position ($this->pos) on a last symbol of the
|
||
|
* constant.
|
||
|
*
|
||
|
* The constant can be:
|
||
|
* - trigger function like {host:item[].func()}; can be without host:item pair
|
||
|
* - floating point number; can be with suffix [KMGTsmhdw]
|
||
|
* - macro like {TRIGGER.VALUE}
|
||
|
* - user macro like {$MACRO}
|
||
|
* - LLD macro like {#LLD}
|
||
|
* - LLD macro with function like {{#LLD}.func())}
|
||
|
*
|
||
|
* @return bool Returns true if parsed successfully, false otherwise.
|
||
|
*/
|
||
|
private function parseConstant() {
|
||
|
if ($this->parseNumber() || $this->parseString()
|
||
|
|| $this->parseUsing($this->user_macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_USER_MACRO)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ($this->options['calculated']) {
|
||
|
if ($this->parseFunction()) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
elseif ($this->parseFunctionMacro()
|
||
|
|| $this->parseUsing($this->macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_MACRO)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// LLD macro support for trigger prototypes.
|
||
|
if ($this->options['lldmacros']) {
|
||
|
if ($this->parseUsing($this->lld_macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)
|
||
|
|| $this->parseUsing($this->lld_macro_function_parser,
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ($this->options['allow_func_only'] && $this->parseFunctionOnly());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a trigger function in the trigger expression and moves a current position ($this->pos) on a last symbol of
|
||
|
* the trigger function.
|
||
|
*
|
||
|
* @return bool Returns true if parsed successfully, false otherwise.
|
||
|
*/
|
||
|
private function parseFunctionOnly() {
|
||
|
$pos = $this->pos;
|
||
|
|
||
|
if ($this->expression[$pos] !== '{') {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$pos++;
|
||
|
|
||
|
if ($this->function_parser->parse($this->expression, $pos) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$pos += $this->function_parser->getLength();
|
||
|
|
||
|
if (isset($this->expression[$pos]) && $this->expression[$pos] !== '}') {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$function_param_list = [];
|
||
|
|
||
|
for ($n = 0; $n < $this->function_parser->getParamsNum(); $n++) {
|
||
|
$function_param_list[] = $this->function_parser->getParam($n);
|
||
|
}
|
||
|
|
||
|
$expression = substr($this->expression, $this->pos, $pos + 1 - $this->pos);
|
||
|
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
|
||
|
$expression, $this->pos, $this->function_parser->getLength() + 2,
|
||
|
[
|
||
|
'host' => '',
|
||
|
'item' => '',
|
||
|
'function' => $this->function_parser->getMatch(),
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParamsRaw' => $this->function_parser->getParamsRaw(),
|
||
|
'functionParams' => $function_param_list
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$this->expressions[] = [
|
||
|
'expression' => $expression,
|
||
|
'pos' => $this->pos,
|
||
|
'host' => '',
|
||
|
'item' => '',
|
||
|
'function' => $this->function_parser->getMatch(),
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParam' => $this->function_parser->getParameters(),
|
||
|
'functionParamList' => $function_param_list
|
||
|
];
|
||
|
|
||
|
$this->pos = $pos;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a trigger function macro constant in the trigger expression and
|
||
|
* moves a current position ($this->pos) on a last symbol of the macro
|
||
|
*
|
||
|
* @return bool returns true if parsed successfully, false otherwise
|
||
|
*/
|
||
|
private function parseFunctionMacro() {
|
||
|
if ($this->options['collapsed_expression']) {
|
||
|
return $this->parseUsing($this->functionid_parser, C10TriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO);
|
||
|
}
|
||
|
else {
|
||
|
return $this->parseSimpleMacro();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a simple macro constant {host:key.func()} in the trigger expression and
|
||
|
* moves a current position ($this->pos) on a last symbol of the macro
|
||
|
*
|
||
|
* @return bool returns true if parsed successfully, false otherwise
|
||
|
*/
|
||
|
private function parseSimpleMacro() {
|
||
|
$startPos = $this->pos;
|
||
|
|
||
|
if ($this->function_macro_parser->parse($this->expression, $this->pos) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ($this->function_parser->parse($this->function_macro_parser->getFunction()) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$this->pos += $this->function_macro_parser->getLength() - 1;
|
||
|
|
||
|
$function_param_list = [];
|
||
|
|
||
|
for ($n = 0; $n < $this->function_parser->getParamsNum(); $n++) {
|
||
|
$function_param_list[] = $this->function_parser->getParam($n);
|
||
|
}
|
||
|
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
|
||
|
$this->function_macro_parser->getMatch(), $startPos, $this->function_macro_parser->getLength(),
|
||
|
[
|
||
|
'host' => $this->function_macro_parser->getHost(),
|
||
|
'item' => $this->function_macro_parser->getItem(),
|
||
|
'function' => $this->function_macro_parser->getFunction(),
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParamsRaw' => $this->function_parser->getParamsRaw(),
|
||
|
'functionParams' => $function_param_list
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$this->expressions[] = [
|
||
|
'expression' => $this->function_macro_parser->getMatch(),
|
||
|
'pos' => $startPos,
|
||
|
'host' => $this->function_macro_parser->getHost(),
|
||
|
'item' => $this->function_macro_parser->getItem(),
|
||
|
'function' => $this->function_macro_parser->getFunction(),
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParam' => $this->function_parser->getParameters(),
|
||
|
'functionParamList' => $function_param_list
|
||
|
];
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a function in the calculated item formula and moves
|
||
|
* a current position ($this->pos) on a last symbol of the macro.
|
||
|
*
|
||
|
* @return bool Returns true if parsed successfully, false otherwise.
|
||
|
*/
|
||
|
private function parseFunction() {
|
||
|
$startPos = $this->pos;
|
||
|
|
||
|
if ($this->function_parser->parse($this->expression, $this->pos) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$this->pos += $this->function_parser->getLength() - 1;
|
||
|
|
||
|
$function_param_list = [];
|
||
|
|
||
|
for ($n = 0; $n < $this->function_parser->getParamsNum(); $n++) {
|
||
|
$function_param_list[] = $this->function_parser->getParam($n);
|
||
|
}
|
||
|
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION,
|
||
|
$this->function_parser->getMatch(), $startPos, $this->function_parser->getLength(),
|
||
|
[
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParamsRaw' => $this->function_parser->getParamsRaw(),
|
||
|
'functionParams' => $function_param_list
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$this->expressions[] = [
|
||
|
'expression' => $this->function_parser->getMatch(),
|
||
|
'pos' => $startPos,
|
||
|
'functionName' => $this->function_parser->getFunction(),
|
||
|
'functionParam' => $this->function_parser->getParameters(),
|
||
|
'functionParamList' => $function_param_list
|
||
|
];
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a number constant in the trigger expression and
|
||
|
* moves a current position ($this->pos) on a last symbol of the number
|
||
|
*
|
||
|
* @return bool returns true if parsed successfully, false otherwise
|
||
|
*/
|
||
|
private function parseNumber() {
|
||
|
if ($this->number_parser->parse($this->expression, $this->pos) == CParser::PARSE_FAIL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$value = $this->number_parser->calcValue();
|
||
|
if (abs($value) == INF) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$token_data = [
|
||
|
'suffix' => $this->number_parser->getSuffix()
|
||
|
];
|
||
|
|
||
|
$this->result->addToken(
|
||
|
C10TriggerExprParserResult::TOKEN_TYPE_NUMBER,
|
||
|
$this->number_parser->getMatch(),
|
||
|
$this->pos,
|
||
|
$this->number_parser->getLength(),
|
||
|
$token_data
|
||
|
);
|
||
|
|
||
|
$this->pos += $this->number_parser->getLength() - 1;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses a quoted string constant in the trigger expression and moves a current position ($this->pos) on a last
|
||
|
* symbol of the string.
|
||
|
*
|
||
|
* @return bool returns true if parsed successfully, false otherwise
|
||
|
*/
|
||
|
private function parseString() {
|
||
|
if (!preg_match('/^"([^"\\\\]|\\\\["\\\\])*"/', substr($this->expression, $this->pos), $matches)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$len = strlen($matches[0]);
|
||
|
|
||
|
$this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_STRING, $matches[0], $this->pos, $len,
|
||
|
['string' => self::unquoteString($matches[0])]
|
||
|
);
|
||
|
|
||
|
$this->pos += $len - 1;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unquoting quoted string $value.
|
||
|
*
|
||
|
* @param string $value
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function unquoteString(string $value): string {
|
||
|
return strtr(substr($value, 1, -1), ['\\"' => '"', '\\\\' => '\\']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Quoting $value if it contains a non numeric value.
|
||
|
*
|
||
|
* @param string $value
|
||
|
* @param bool $allow_macros
|
||
|
* @param bool $force
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function quoteString(string $value, bool $allow_macros = true, bool $force = false): string {
|
||
|
if (!$force) {
|
||
|
$number_parser = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
|
||
|
|
||
|
if ($number_parser->parse($value) == CParser::PARSE_SUCCESS) {
|
||
|
return $value;
|
||
|
}
|
||
|
|
||
|
if ($allow_macros) {
|
||
|
$user_macro_parser = new CUserMacroParser();
|
||
|
$macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
|
||
|
$lld_macro_parser = new CLLDMacroParser();
|
||
|
$lld_macro_function_parser = new CLLDMacroFunctionParser;
|
||
|
|
||
|
if ($user_macro_parser->parse($value) == CParser::PARSE_SUCCESS
|
||
|
|| $macro_parser->parse($value) == CParser::PARSE_SUCCESS
|
||
|
|| $lld_macro_parser->parse($value) == CParser::PARSE_SUCCESS
|
||
|
|| $lld_macro_function_parser->parse($value) == CParser::PARSE_SUCCESS) {
|
||
|
return $value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return '"'.strtr($value, ['\\' => '\\\\', '"' => '\\"']).'"';
|
||
|
}
|
||
|
}
|