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.
2995 lines
92 KiB
2995 lines
92 KiB
<?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.
|
|
**/
|
|
|
|
|
|
class CMacrosResolverGeneral {
|
|
|
|
/**
|
|
* Interface priorities.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected const interfacePriorities = [
|
|
INTERFACE_TYPE_AGENT => 4,
|
|
INTERFACE_TYPE_SNMP => 3,
|
|
INTERFACE_TYPE_JMX => 2,
|
|
INTERFACE_TYPE_IPMI => 1
|
|
];
|
|
|
|
protected const aggr_triggers_macros = ['{TRIGGER.EVENTS.ACK}', '{TRIGGER.EVENTS.PROBLEM.ACK}',
|
|
'{TRIGGER.EVENTS.PROBLEM.UNACK}', '{TRIGGER.EVENTS.UNACK}', '{TRIGGER.PROBLEM.EVENTS.PROBLEM.ACK}',
|
|
'{TRIGGER.PROBLEM.EVENTS.PROBLEM.UNACK}', '{TRIGGERS.UNACK}', '{TRIGGERS.PROBLEM.UNACK}', '{TRIGGERS.ACK}',
|
|
'{TRIGGERS.PROBLEM.ACK}'];
|
|
|
|
/**
|
|
* Work config name.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $config = '';
|
|
|
|
/**
|
|
* Get reference macros for trigger.
|
|
* If macro reference non existing value it expands to empty string.
|
|
*
|
|
* @param string $expression
|
|
* @param array $references
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function resolveTriggerReferences($expression, $references) {
|
|
$values = [];
|
|
$expression_parser = new CExpressionParser([
|
|
'usermacros' => true,
|
|
'lldmacros' => true,
|
|
'collapsed_expression' => true
|
|
]);
|
|
|
|
if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
|
|
foreach ($expression_parser->getResult()->getTokens() as $token) {
|
|
switch ($token['type']) {
|
|
case CExpressionParserResult::TOKEN_TYPE_NUMBER:
|
|
case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
|
|
$values[] = $token['match'];
|
|
break;
|
|
|
|
case CExpressionParserResult::TOKEN_TYPE_STRING:
|
|
$values[] = CExpressionParser::unquoteString($token['match']);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($references as $macro => $value) {
|
|
$i = (int) $macro[1] - 1;
|
|
$references[$macro] = array_key_exists($i, $values) ? $values[$i] : '';
|
|
}
|
|
|
|
return $references;
|
|
}
|
|
|
|
/**
|
|
* Checking existence of the macros.
|
|
*
|
|
* @param array $texts
|
|
* @param array $type
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function hasMacros(array $texts, array $types) {
|
|
foreach ($texts as $text) {
|
|
if (self::getMacroPositions($text, $types)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Transform types, used in extractMacros() function to types which can be used in getMacroPositions().
|
|
*
|
|
* @param array $types
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function transformToPositionTypes(array $types) {
|
|
foreach (['macros', 'macro_funcs', 'macros_n', 'macros_an', 'macro_funcs_n'] as $type) {
|
|
if (array_key_exists($type, $types)) {
|
|
$patterns = [];
|
|
foreach ($types[$type] as $key => $_patterns) {
|
|
$patterns = array_merge($patterns, $_patterns);
|
|
}
|
|
$types[$type] = $patterns;
|
|
}
|
|
}
|
|
|
|
return $types;
|
|
}
|
|
|
|
/**
|
|
* Extract positions of the macros from a string.
|
|
*
|
|
* @param string $text
|
|
* @param array $types
|
|
* @param bool $types['usermacros']
|
|
* @param array $types['macros'][<macro_patterns>]
|
|
* @param array $types['macro_funcs'][<macro_patterns>]
|
|
* @param array $types['macros_n'][<macro_patterns>]
|
|
* @param array $types['macros_an'][<macro_patterns>]
|
|
* @param array $types['macro_funcs_n'][<macro_patterns>]
|
|
* @param bool $types['references']
|
|
* @param bool $types['lldmacros']
|
|
* @param bool $types['functionids']
|
|
* @param bool $types['replacements']
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getMacroPositions($text, array $types) {
|
|
$macros = [];
|
|
$extract_usermacros = array_key_exists('usermacros', $types);
|
|
$extract_macros = array_key_exists('macros', $types);
|
|
$extract_macro_funcs = array_key_exists('macro_funcs', $types);
|
|
$extract_macros_n = array_key_exists('macros_n', $types);
|
|
$extract_macros_an = array_key_exists('macros_an', $types);
|
|
$extract_macro_funcs_n = array_key_exists('macro_funcs_n', $types);
|
|
$extract_references = array_key_exists('references', $types);
|
|
$extract_lldmacros = array_key_exists('lldmacros', $types);
|
|
$extract_functionids = array_key_exists('functionids', $types);
|
|
$extract_replacements = array_key_exists('replacements', $types);
|
|
|
|
if ($extract_usermacros) {
|
|
$user_macro_parser = new CUserMacroParser();
|
|
}
|
|
|
|
if ($extract_macros) {
|
|
$macro_parser = new CMacroParser(['macros' => $types['macros']]);
|
|
}
|
|
|
|
if ($extract_macro_funcs) {
|
|
$macro_func_parser = new CMacroFunctionParser(['macros' => $types['macro_funcs']]);
|
|
}
|
|
|
|
if ($extract_macros_n) {
|
|
$macro_n_parser = new CMacroParser([
|
|
'macros' => $types['macros_n'],
|
|
'ref_type' => CMacroParser::REFERENCE_NUMERIC
|
|
]);
|
|
}
|
|
|
|
if ($extract_macros_an) {
|
|
$macro_an_parser = new CMacroParser([
|
|
'macros' => $types['macros_an'],
|
|
'ref_type' => CMacroParser::REFERENCE_ALPHANUMERIC
|
|
]);
|
|
}
|
|
|
|
if ($extract_macro_funcs_n) {
|
|
$macro_func_n_parser = new CMacroFunctionParser([
|
|
'macros' => $types['macro_funcs_n'],
|
|
'ref_type' => CMacroParser::REFERENCE_NUMERIC
|
|
]);
|
|
}
|
|
|
|
if ($extract_references) {
|
|
$reference_parser = new CReferenceParser();
|
|
}
|
|
|
|
if ($extract_lldmacros) {
|
|
$lld_macro_parser = new CLLDMacroParser();
|
|
$lld_macro_function_parser = new CLLDMacroFunctionParser();
|
|
}
|
|
|
|
if ($extract_functionids) {
|
|
$functionid_parser = new CFunctionIdParser();
|
|
}
|
|
|
|
if ($extract_replacements) {
|
|
$replacement_parser = new CReplacementParser();
|
|
}
|
|
|
|
for ($pos = 0; isset($text[$pos]); $pos++) {
|
|
if ($extract_usermacros && $user_macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $user_macro_parser->getMatch();
|
|
$pos += $user_macro_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_macros && $macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $macro_parser->getMatch();
|
|
$pos += $macro_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_macro_funcs && $macro_func_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $macro_func_parser->getMatch();
|
|
$pos += $macro_func_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_macros_n && $macro_n_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $macro_n_parser->getMatch();
|
|
$pos += $macro_n_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_macros_an && $macro_an_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $macro_an_parser->getMatch();
|
|
$pos += $macro_an_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_macro_funcs_n && $macro_func_n_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $macro_func_n_parser->getMatch();
|
|
$pos += $macro_func_n_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_references && $reference_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $reference_parser->getMatch();
|
|
$pos += $reference_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_lldmacros && $lld_macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $lld_macro_parser->getMatch();
|
|
$pos += $lld_macro_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_lldmacros && $lld_macro_function_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $lld_macro_function_parser->getMatch();
|
|
$pos += $lld_macro_function_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_functionids && $functionid_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $functionid_parser->getMatch();
|
|
$pos += $functionid_parser->getLength() - 1;
|
|
}
|
|
elseif ($extract_replacements && $replacement_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros[$pos] = $replacement_parser->getMatch();
|
|
$pos += $replacement_parser->getLength() - 1;
|
|
}
|
|
}
|
|
|
|
return $macros;
|
|
}
|
|
|
|
/**
|
|
* Returns true if parsed expression is calculable.
|
|
*
|
|
* @param array $tokens
|
|
*
|
|
* @return bool
|
|
*/
|
|
private static function isCalculableExpression(array $tokens): bool {
|
|
if (count($tokens) != 1 || $tokens[0]['type'] != CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION) {
|
|
return false;
|
|
}
|
|
|
|
$expression_validator = new CExpressionValidator();
|
|
|
|
if (!$expression_validator->validate($tokens)) {
|
|
return false;
|
|
}
|
|
|
|
if (!in_array($tokens[0]['data']['function'], ['last', 'min', 'max', 'avg'])) {
|
|
return false;
|
|
}
|
|
|
|
$parameters = $tokens[0]['data']['parameters'];
|
|
|
|
// Time shift is not supported.
|
|
if (array_key_exists(1, $parameters) && ($parameters[1]['type'] != CHistFunctionParser::PARAM_TYPE_PERIOD
|
|
|| $parameters[1]['data']['sec_num'][0] === '#' || $parameters[1]['data']['time_shift'] !== '')) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Extract macros from a string.
|
|
*
|
|
* @param array $texts
|
|
* @param array $types
|
|
* @param bool $types['usermacros'] Extract user macros. For example, "{$MACRO}".
|
|
* @param array $types['macros'][][<macro_patterns>] Extract macros. For example, "{HOST.HOST}".
|
|
* @param array $types['macro_funcs'][][<macro_patterns>] Extract macros with macro function.
|
|
* For example, "{{ITEM.VALUE}.func(param)}".
|
|
* @param array $types['macros_n'][][<macro_patterns>] Extract macros with optional numeric index.
|
|
* For example, "{HOST.HOST<1-9>}".
|
|
* @param array $types['macros_an'][][<macro_patterns>] Extract macros with optional numeric or alphabetic
|
|
* index. For example, "{EVENT.TAGS.Service}".
|
|
* @param array $types['macro_funcs_n'][][<macro_patterns>] Extract macros with optional numeric index and macro
|
|
* function.
|
|
* For example, "{{ITEM.VALUE<1-9>}.func(param)}".
|
|
* @param bool $types['references'] Extract dollar-sign references. For example, "$5".
|
|
* @param bool $types['lldmacros'] Extract low-level discovery macros.
|
|
* For example, "{#LLD.MACRO}".
|
|
* @param bool $types['functionids'] Extract numeric macros. For example, "{12345}".
|
|
* @param bool $types['expr_macros'] Extract expression macros.
|
|
* For example, "{?func(/host/key, param)}".
|
|
* @param bool $types['expr_macros_host'] Extract expression macros with the ability to
|
|
* specify a {HOST.HOST} macro or an empty host name
|
|
* instead of a hostname.
|
|
* For example,
|
|
* "{?func(/host/key, param)}",
|
|
* "{?func(/{HOST.HOST}/key, param)}",
|
|
* "{?func(//key, param)}".
|
|
* @param bool $types['expr_macros_host_n'] Extract expression macros with the ability to
|
|
* specify a {HOST.HOST<1-9>} macro or an empty host
|
|
* name instead of a hostname.
|
|
* For example,
|
|
* "{?func(/host/key, param)}",
|
|
* "{?func(/{HOST.HOST}/key, param)}",
|
|
* "{?func(/{HOST.HOST5}/key, param)}",
|
|
* "{?func(//key, param)}".
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function extractMacros(array $texts, array $types) {
|
|
$macros = [];
|
|
$extract_usermacros = array_key_exists('usermacros', $types);
|
|
$extract_macros = array_key_exists('macros', $types);
|
|
$extract_macro_funcs = array_key_exists('macro_funcs', $types);
|
|
$extract_macros_n = array_key_exists('macros_n', $types);
|
|
$extract_macros_an = array_key_exists('macros_an', $types);
|
|
$extract_macro_funcs_n = array_key_exists('macro_funcs_n', $types);
|
|
$extract_references = array_key_exists('references', $types);
|
|
$extract_lldmacros = array_key_exists('lldmacros', $types);
|
|
$extract_functionids = array_key_exists('functionids', $types);
|
|
$extract_expr_macros = array_key_exists('expr_macros', $types);
|
|
$extract_expr_macros_host = array_key_exists('expr_macros_host', $types);
|
|
$extract_expr_macros_host_n = array_key_exists('expr_macros_host_n', $types);
|
|
|
|
if ($extract_usermacros) {
|
|
$macros['usermacros'] = [];
|
|
|
|
$user_macro_parser = new CUserMacroParser();
|
|
}
|
|
|
|
if ($extract_macros) {
|
|
$macros['macros'] = [];
|
|
|
|
foreach ($types['macros'] as $key => $macro_patterns) {
|
|
$types['macros'][$key] = new CMacroParser(['macros' => $macro_patterns]);
|
|
$macros['macros'][$key] = [];
|
|
}
|
|
}
|
|
|
|
if ($extract_macro_funcs) {
|
|
$macros['macro_funcs'] = [];
|
|
|
|
foreach ($types['macro_funcs'] as $key => $macro_patterns) {
|
|
$types['macro_funcs'][$key] = new CMacroFunctionParser(['macros' => $macro_patterns]);
|
|
$macros['macro_funcs'][$key] = [];
|
|
}
|
|
}
|
|
|
|
if ($extract_macros_n) {
|
|
$macros['macros_n'] = [];
|
|
|
|
foreach ($types['macros_n'] as $key => $macro_patterns) {
|
|
$types['macros_n'][$key] = new CMacroParser([
|
|
'macros' => $macro_patterns,
|
|
'ref_type' => CMacroParser::REFERENCE_NUMERIC
|
|
]);
|
|
$macros['macros_n'][$key] = [];
|
|
}
|
|
}
|
|
|
|
if ($extract_macros_an) {
|
|
$macros['macros_an'] = [];
|
|
|
|
foreach ($types['macros_an'] as $key => $macro_patterns) {
|
|
$types['macros_an'][$key] = new CMacroParser([
|
|
'macros' => $macro_patterns,
|
|
'ref_type' => CMacroParser::REFERENCE_ALPHANUMERIC
|
|
]);
|
|
$macros['macros_an'][$key] = [];
|
|
}
|
|
}
|
|
|
|
if ($extract_macro_funcs_n) {
|
|
$macros['macro_funcs_n'] = [];
|
|
|
|
foreach ($types['macro_funcs_n'] as $key => $macro_patterns) {
|
|
$types['macro_funcs_n'][$key] = new CMacroFunctionParser([
|
|
'macros' => $macro_patterns,
|
|
'ref_type' => CMacroParser::REFERENCE_NUMERIC
|
|
]);
|
|
$macros['macro_funcs_n'][$key] = [];
|
|
}
|
|
}
|
|
|
|
if ($extract_references) {
|
|
$macros['references'] = [];
|
|
|
|
$reference_parser = new CReferenceParser();
|
|
}
|
|
|
|
if ($extract_lldmacros) {
|
|
$macros['lldmacros'] = [];
|
|
|
|
$lld_macro_parser = new CLLDMacroParser();
|
|
$lld_macro_function_parser = new CLLDMacroFunctionParser();
|
|
}
|
|
|
|
if ($extract_functionids) {
|
|
$macros['functionids'] = [];
|
|
|
|
$functionid_parser = new CFunctionIdParser();
|
|
}
|
|
|
|
if ($extract_expr_macros) {
|
|
$macros['expr_macros'] = [];
|
|
|
|
$expr_macro_parser = new CExpressionMacroParser();
|
|
$expr_macro_function_parser = new CExpressionMacroFunctionParser();
|
|
}
|
|
|
|
if ($extract_expr_macros_host) {
|
|
$macros['expr_macros_host'] = [];
|
|
$options = ['host_macro' => true, 'empty_host' => true];
|
|
|
|
$expr_macro_parser_host = new CExpressionMacroParser($options);
|
|
$expr_macro_function_parser_host = new CExpressionMacroFunctionParser($options);
|
|
}
|
|
|
|
if ($extract_expr_macros_host_n) {
|
|
$macros['expr_macros_host_n'] = [];
|
|
$options = ['host_macro_n' => true, 'empty_host' => true];
|
|
|
|
$expr_macro_parser_host_n = new CExpressionMacroParser($options);
|
|
$expr_macro_function_parser_host_n = new CExpressionMacroFunctionParser($options);
|
|
}
|
|
|
|
foreach ($texts as $text) {
|
|
for ($pos = 0; isset($text[$pos]); $pos++) {
|
|
if ($extract_usermacros && $user_macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['usermacros'][$user_macro_parser->getMatch()] = null;
|
|
$pos += $user_macro_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
|
|
if ($extract_macros) {
|
|
foreach ($types['macros'] as $key => $macro_parser) {
|
|
if ($macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['macros'][$key][$macro_parser->getMatch()] = true;
|
|
$pos += $macro_parser->getLength() - 1;
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_macro_funcs) {
|
|
foreach ($types['macro_funcs'] as $key => $macro_func_parser) {
|
|
if ($macro_func_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macro_parser = $macro_func_parser->getMacroParser();
|
|
$function_parser = $macro_func_parser->getFunctionParser();
|
|
|
|
$macros['macro_funcs'][$key][$macro_func_parser->getMatch()] = [
|
|
'macro' => $macro_parser->getMacro(),
|
|
'macrofunc' => [
|
|
'function' => $function_parser->getFunction(),
|
|
'parameters' => $function_parser->getParams()
|
|
]
|
|
];
|
|
$pos += $macro_func_parser->getLength() - 1;
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_macros_n) {
|
|
foreach ($types['macros_n'] as $key => $macro_n_parser) {
|
|
if ($macro_n_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['macros_n'][$key][$macro_n_parser->getMatch()] = [
|
|
'macro' => $macro_n_parser->getMacro(),
|
|
'f_num' => $macro_n_parser->getReference()
|
|
];
|
|
$pos += $macro_n_parser->getLength() - 1;
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_macros_an) {
|
|
foreach ($types['macros_an'] as $key => $macro_an_parser) {
|
|
if ($macro_an_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['macros_an'][$key][$macro_an_parser->getMatch()] = [
|
|
'macro' => $macro_an_parser->getMacro(),
|
|
'f_num' => $macro_an_parser->getReference()
|
|
];
|
|
$pos += $macro_an_parser->getLength() - 1;
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_macro_funcs_n) {
|
|
foreach ($types['macro_funcs_n'] as $key => $macro_func_n_parser) {
|
|
if ($macro_func_n_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macro_n_parser = $macro_func_n_parser->getMacroParser();
|
|
$function_parser = $macro_func_n_parser->getFunctionParser();
|
|
|
|
$macros['macro_funcs_n'][$key][$macro_func_n_parser->getMatch()] = [
|
|
'macro' => $macro_n_parser->getMacro(),
|
|
'f_num' => $macro_n_parser->getReference(),
|
|
'macrofunc' => [
|
|
'function' => $function_parser->getFunction(),
|
|
'parameters' => $function_parser->getParams()
|
|
]
|
|
];
|
|
$pos += $macro_func_n_parser->getLength() - 1;
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_references && $reference_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['references'][$reference_parser->getMatch()] = null;
|
|
$pos += $reference_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
|
|
if ($extract_lldmacros) {
|
|
if ($lld_macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['lldmacros'][$lld_macro_parser->getMatch()] = null;
|
|
$pos += $lld_macro_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
elseif ($lld_macro_function_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['lldmacros'][$lld_macro_function_parser->getMatch()] = null;
|
|
$pos += $lld_macro_function_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_functionids && $functionid_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$macros['functionids'][$functionid_parser->getMatch()] = null;
|
|
$pos += $functionid_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
|
|
if ($extract_expr_macros && $expr_macro_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_parser
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$macros['expr_macros'][$expr_macro_parser->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: ''
|
|
];
|
|
$pos += $expr_macro_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_expr_macros && $expr_macro_function_parser->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_function_parser
|
|
->getExpressionMacroParser()
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$function_parser = $expr_macro_function_parser->getFunctionParser();
|
|
|
|
$macros['expr_macros'][$expr_macro_function_parser->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: '',
|
|
'macrofunc' => [
|
|
'function' => $function_parser->getFunction(),
|
|
'parameters' => $function_parser->getParams()
|
|
]
|
|
];
|
|
$pos += $expr_macro_function_parser->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_expr_macros_host && $expr_macro_parser_host->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_parser_host
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$macros['expr_macros_host'][$expr_macro_parser_host->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: ''
|
|
];
|
|
$pos += $expr_macro_parser_host->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_expr_macros_host
|
|
&& $expr_macro_function_parser_host->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_function_parser_host
|
|
->getExpressionMacroParser()
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$function_parser = $expr_macro_function_parser_host->getFunctionParser();
|
|
|
|
$macros['expr_macros_host'][$expr_macro_function_parser_host->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: '',
|
|
'macrofunc' => [
|
|
'function' => $function_parser->getFunction(),
|
|
'parameters' => $function_parser->getParams()
|
|
]
|
|
|
|
];
|
|
$pos += $expr_macro_function_parser_host->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_expr_macros_host_n
|
|
&& $expr_macro_parser_host_n->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_parser_host_n
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$macros['expr_macros_host_n'][$expr_macro_parser_host_n->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: ''
|
|
];
|
|
$pos += $expr_macro_parser_host_n->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($extract_expr_macros_host_n
|
|
&& $expr_macro_function_parser_host_n->parse($text, $pos) != CParser::PARSE_FAIL) {
|
|
$tokens = $expr_macro_function_parser_host_n
|
|
->getExpressionMacroParser()
|
|
->getExpressionParser()
|
|
->getResult()
|
|
->getTokens();
|
|
|
|
if (self::isCalculableExpression($tokens)) {
|
|
$function_parser = $expr_macro_function_parser_host_n->getFunctionParser();
|
|
|
|
$macros['expr_macros_host_n'][$expr_macro_function_parser_host_n->getMatch()] = [
|
|
'function' => $tokens[0]['data']['function'],
|
|
'host' => $tokens[0]['data']['parameters'][0]['data']['host'],
|
|
'key' => $tokens[0]['data']['parameters'][0]['data']['item'],
|
|
'sec_num' => array_key_exists(1, $tokens[0]['data']['parameters'])
|
|
? $tokens[0]['data']['parameters'][1]['data']['sec_num']
|
|
: '',
|
|
'macrofunc' => [
|
|
'function' => $function_parser->getFunction(),
|
|
'parameters' => $function_parser->getParams()
|
|
]
|
|
];
|
|
$pos += $expr_macro_function_parser_host_n->getLength() - 1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($extract_macros) {
|
|
foreach ($types['macros'] as $key => $macro_parser) {
|
|
$macros['macros'][$key] = array_keys($macros['macros'][$key]);
|
|
}
|
|
}
|
|
|
|
return $macros;
|
|
}
|
|
|
|
/**
|
|
* Returns the list of the item key parameters.
|
|
*
|
|
* @param array $params_raw
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getItemKeyParameters($params_raw) {
|
|
$item_key_parameters = [];
|
|
|
|
foreach ($params_raw as $param_raw) {
|
|
switch ($param_raw['type']) {
|
|
case CItemKey::PARAM_ARRAY:
|
|
$item_key_parameters = array_merge($item_key_parameters,
|
|
self::getItemKeyParameters($param_raw['parameters'])
|
|
);
|
|
break;
|
|
|
|
case CItemKey::PARAM_UNQUOTED:
|
|
$item_key_parameters[] = $param_raw['raw'];
|
|
break;
|
|
|
|
case CItemKey::PARAM_QUOTED:
|
|
$item_key_parameters[] = CItemKey::unquoteParam($param_raw['raw']);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $item_key_parameters;
|
|
}
|
|
|
|
/**
|
|
* Extract macros from an item key.
|
|
*
|
|
* @param string $key an item key
|
|
* @param array $types the types of macros (see extractMacros() for more details)
|
|
*
|
|
* @return array see extractMacros() for more details
|
|
*/
|
|
protected function extractItemKeyMacros($key, array $types) {
|
|
$item_key_parser = new CItemKey();
|
|
|
|
$item_key_parameters = [];
|
|
if ($item_key_parser->parse($key) == CParser::PARSE_SUCCESS) {
|
|
$item_key_parameters = self::getItemKeyParameters($item_key_parser->getParamsRaw());
|
|
}
|
|
|
|
return self::extractMacros($item_key_parameters, $types);
|
|
}
|
|
|
|
/**
|
|
* Extract macros from a trigger function.
|
|
*
|
|
* @param string $function a history function, for example 'last(/host/key, {$OFFSET})'
|
|
* @param array $types the types of macros (see extractMacros() for more details)
|
|
*
|
|
* @return array see extractMacros() for more details
|
|
*/
|
|
protected function extractFunctionMacros($function, array $types) {
|
|
$hist_function_parser = new CHistFunctionParser(['usermacros' => true, 'lldmacros' => true]);
|
|
$function_parameters = [];
|
|
|
|
if ($hist_function_parser->parse($function) == CParser::PARSE_SUCCESS) {
|
|
foreach ($hist_function_parser->getParameters() as $parameter) {
|
|
switch ($parameter['type']) {
|
|
case CHistFunctionParser::PARAM_TYPE_PERIOD:
|
|
case CHistFunctionParser::PARAM_TYPE_UNQUOTED:
|
|
$function_parameters[] = $parameter['match'];
|
|
break;
|
|
|
|
case CHistFunctionParser::PARAM_TYPE_QUOTED:
|
|
$function_parameters[] = CHistFunctionParser::unquoteParam($parameter['match']);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return self::extractMacros($function_parameters, $types);
|
|
}
|
|
|
|
/**
|
|
* Resolves macros in the item key parameters.
|
|
*
|
|
* @param string $key_chain an item key chain
|
|
* @param array $params_raw
|
|
* @param array $macros the list of macros (['{<MACRO>}' => '<value>', ...])
|
|
* @param array $types the types of macros (see getMacroPositions() for more details)
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function resolveItemKeyParamsMacros($key_chain, array $params_raw, array $macros, array $types) {
|
|
foreach (array_reverse($params_raw) as $param_raw) {
|
|
$param = $param_raw['raw'];
|
|
$forced = false;
|
|
|
|
switch ($param_raw['type']) {
|
|
case CItemKey::PARAM_ARRAY:
|
|
$param = self::resolveItemKeyParamsMacros($param, $param_raw['parameters'], $macros, $types);
|
|
break;
|
|
|
|
case CItemKey::PARAM_QUOTED:
|
|
$param = CItemKey::unquoteParam($param);
|
|
$forced = true;
|
|
// break; is not missing here
|
|
|
|
case CItemKey::PARAM_UNQUOTED:
|
|
$param = quoteItemKeyParam(strtr($param, $macros), $forced);
|
|
break;
|
|
}
|
|
|
|
$key_chain = substr_replace($key_chain, $param, $param_raw['pos'], strlen($param_raw['raw']));
|
|
}
|
|
|
|
return $key_chain;
|
|
}
|
|
|
|
/**
|
|
* Resolves macros in the item key.
|
|
*
|
|
* @param string $key an item key
|
|
* @param array $macros the list of macros (['{<MACRO>}' => '<value>', ...])
|
|
* @param array $types the types of macros (see getMacroPositions() for more details)
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function resolveItemKeyMacros($key, array $macros, array $types) {
|
|
$item_key_parser = new CItemKey();
|
|
|
|
if ($item_key_parser->parse($key) == CParser::PARSE_SUCCESS) {
|
|
$key = self::resolveItemKeyParamsMacros($key, $item_key_parser->getParamsRaw(), $macros, $types);
|
|
}
|
|
|
|
return $key;
|
|
}
|
|
|
|
/**
|
|
* Resolves macros in the trigger function parameters.
|
|
*
|
|
* @param string $function a trigger function
|
|
* @param array $macros the list of macros (['{<MACRO>}' => '<value>', ...])
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function resolveFunctionMacros($function, array $macros) {
|
|
$hist_function_parser = new CHistFunctionParser(['usermacros' => true, 'lldmacros' => true]);
|
|
|
|
if ($hist_function_parser->parse($function) == CParser::PARSE_SUCCESS) {
|
|
foreach (array_reverse($hist_function_parser->getParameters(), true) as $i => $parameter) {
|
|
switch ($parameter['type']) {
|
|
case CHistFunctionParser::PARAM_TYPE_PERIOD:
|
|
case CHistFunctionParser::PARAM_TYPE_UNQUOTED:
|
|
case CHistFunctionParser::PARAM_TYPE_QUOTED:
|
|
$param = strtr($hist_function_parser->getParam($i), $macros);
|
|
|
|
if ($parameter['type'] != CHistFunctionParser::PARAM_TYPE_PERIOD) {
|
|
$param = CExpressionParser::quoteString($param, true,
|
|
$parameter['type'] == CHistFunctionParser::PARAM_TYPE_QUOTED
|
|
);
|
|
}
|
|
|
|
$function = substr_replace($function, $param, $parameter['pos'], $parameter['length']);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $function;
|
|
}
|
|
|
|
/**
|
|
* Find function ids in trigger expression.
|
|
*
|
|
* @param string $expression
|
|
*
|
|
* @return array where key is function id position in expression and value is function id
|
|
*/
|
|
protected function findFunctions($expression) {
|
|
$functionids = [];
|
|
|
|
$functionid_parser = new CFunctionIdParser();
|
|
$macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
|
|
$user_macro_parser = new CUserMacroParser();
|
|
|
|
for ($pos = 0, $i = 1; isset($expression[$pos]); $pos++) {
|
|
if ($functionid_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
|
|
$pos += $functionid_parser->getLength() - 1;
|
|
$functionids[$i++] = substr($functionid_parser->getMatch(), 1, -1);
|
|
}
|
|
elseif ($user_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
|
|
$pos += $user_macro_parser->getLength() - 1;
|
|
}
|
|
elseif ($macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
|
|
$pos += $macro_parser->getLength() - 1;
|
|
}
|
|
}
|
|
|
|
if (array_key_exists(1, $functionids)) {
|
|
$functionids[0] = $functionids[1];
|
|
}
|
|
|
|
return $functionids;
|
|
}
|
|
|
|
/**
|
|
* Get interface macros.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<functionid>]
|
|
* @param array $macros[<functionid>][<macro>] an array of the tokens
|
|
* @param array $macro_values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getIpMacros(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$result = DBselect(
|
|
'SELECT f.triggerid,f.functionid,n.ip,n.dns,n.type,n.useip,n.port'.
|
|
' FROM functions f'.
|
|
' JOIN items i ON f.itemid=i.itemid'.
|
|
' JOIN interface n ON i.hostid=n.hostid'.
|
|
' WHERE '.dbConditionInt('f.functionid', array_keys($macros)).
|
|
' AND n.main=1'
|
|
);
|
|
|
|
// Macro should be resolved to interface with highest priority ($priorities).
|
|
$interfaces = [];
|
|
|
|
while ($row = DBfetch($result)) {
|
|
if (array_key_exists($row['functionid'], $interfaces)
|
|
&& self::interfacePriorities[$interfaces[$row['functionid']]['type']]
|
|
> self::interfacePriorities[$row['type']]) {
|
|
continue;
|
|
}
|
|
|
|
$interfaces[$row['functionid']] = $row;
|
|
}
|
|
|
|
foreach ($interfaces as $interface) {
|
|
foreach ($macros[$interface['functionid']] as $macro => $tokens) {
|
|
switch ($macro) {
|
|
case 'IPADDRESS':
|
|
case 'HOST.IP':
|
|
$value = $interface['ip'];
|
|
break;
|
|
case 'HOST.DNS':
|
|
$value = $interface['dns'];
|
|
break;
|
|
case 'HOST.CONN':
|
|
$value = $interface['useip'] ? $interface['ip'] : $interface['dns'];
|
|
break;
|
|
case 'HOST.PORT':
|
|
$value = $interface['port'];
|
|
break;
|
|
}
|
|
|
|
foreach ($tokens as $token) {
|
|
$macro_values[$interface['triggerid']][$token['token']] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Resolves items value maps, valuemap property will be added to every item.
|
|
*
|
|
* @param array $items
|
|
* @param int $items[]['itemid']
|
|
* @param int $items[]['valuemapid']
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getItemsValueMaps(array $items): array {
|
|
foreach ($items as &$item) {
|
|
$item['valuemap'] = [];
|
|
}
|
|
unset($item);
|
|
|
|
$valuemapids = array_flip(array_column($items, 'valuemapid'));
|
|
unset($valuemapids[0]);
|
|
|
|
if (!$valuemapids) {
|
|
return $items;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['valuemapid', 'type', 'value', 'newvalue'],
|
|
'filter' => ['valuemapid' => array_keys($valuemapids)],
|
|
'sortfield' => ['sortorder']
|
|
];
|
|
$db_mappings = DBselect(DB::makeSql('valuemap_mapping', $options));
|
|
|
|
$db_valuemaps = [];
|
|
|
|
while ($db_mapping = DBfetch($db_mappings)) {
|
|
$db_valuemaps[$db_mapping['valuemapid']]['mappings'][] = [
|
|
'type' => $db_mapping['type'],
|
|
'value' => $db_mapping['value'],
|
|
'newvalue' => $db_mapping['newvalue']
|
|
];
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
if (array_key_exists($item['valuemapid'], $db_valuemaps)) {
|
|
$item['valuemap'] = $db_valuemaps[$item['valuemapid']];
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Get item macros by itemid.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<itemid>]
|
|
* @param array $macros[<itemid>][<key>]
|
|
* @param array $macro_values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getItemMacrosByItemid(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_items = API::Item()->get([
|
|
'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type', 'state', 'description'],
|
|
'itemids' => array_keys($macros),
|
|
'webitems' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$db_items = CMacrosResolverHelper::resolveItemKeys($db_items);
|
|
$db_items = CMacrosResolverHelper::resolveItemDescriptions($db_items);
|
|
|
|
foreach ($db_items as &$db_item) {
|
|
$db_item['state'] = itemState($db_item['state']);
|
|
}
|
|
unset($db_item);
|
|
|
|
$item_macros = ['{ITEM.DESCRIPTION}' => 'description_expanded', '{ITEM.DESCRIPTION.ORIG}' => 'description',
|
|
'{ITEM.KEY}' => 'key_expanded', '{ITEM.KEY.ORIG}' => 'key_', '{ITEM.NAME}' => 'name',
|
|
'{ITEM.NAME.ORIG}' => 'name', '{ITEM.STATE}' => 'state', '{ITEM.VALUETYPE}' => 'value_type'
|
|
];
|
|
|
|
foreach ($macros as $itemid => $keys) {
|
|
if (!array_key_exists($itemid, $db_items)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item_macros as $macro => $field) {
|
|
foreach (array_keys($keys) as $key) {
|
|
if (array_key_exists($macro, $macro_values[$key])) {
|
|
$macro_values[$key][$macro] = $db_items[$itemid][$field];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get item value macros by itemid.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<itemid>]
|
|
* @param array $macros[<itemid>][<key>]
|
|
* @param array $macros[<itemid>][<key>][<token>]
|
|
* @param string $macros[<itemid>][<key>][<token>]['macro']
|
|
* @param string $macros[<itemid>][<key>][<token>]['function']
|
|
* @param array $macros[<itemid>][<key>][<token>]['parameters']
|
|
* @param array $macro_values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getItemValueMacrosByItemid(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_items = API::Item()->get([
|
|
'output' => ['itemid', 'value_type', 'units', 'valuemapid'],
|
|
'itemids' => array_keys($macros),
|
|
'webitems' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
$db_items = self::getItemsValueMaps($db_items);
|
|
|
|
$history = Manager::History()->getLastValues($db_items, 1, timeUnitToSeconds(
|
|
CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD)
|
|
));
|
|
|
|
foreach ($macros as $itemid => $keys) {
|
|
if (!array_key_exists($itemid, $history)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $tokens) {
|
|
foreach ($tokens as $token => $data) {
|
|
switch ($data['macro']) {
|
|
case 'ITEM.VALUE':
|
|
case 'ITEM.LASTVALUE':
|
|
if (array_key_exists('function', $data)) {
|
|
if ($data['function'] !== 'regsub' && $data['function'] !== 'iregsub') {
|
|
continue 2;
|
|
}
|
|
|
|
if (count($data['parameters']) != 2) {
|
|
continue 2;
|
|
}
|
|
|
|
$ci = ($data['function'] === 'iregsub') ? 'i' : '';
|
|
|
|
set_error_handler(function ($errno, $errstr) {});
|
|
$rc = preg_match('/'.$data['parameters'][0].'/'.$ci, $history[$itemid][0]['value'],
|
|
$matches
|
|
);
|
|
restore_error_handler();
|
|
|
|
if ($rc === false) {
|
|
continue 2;
|
|
}
|
|
|
|
$macro_value = $data['parameters'][1];
|
|
$matched_macros = self::getMacroPositions($macro_value, ['replacements' => true]);
|
|
|
|
foreach (array_reverse($matched_macros, true) as $pos => $macro) {
|
|
$macro_value = substr_replace($macro_value,
|
|
array_key_exists($macro[1], $matches) ? $matches[$macro[1]] : '',
|
|
$pos, strlen($macro)
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
$macro_value = formatHistoryValue($history[$itemid][0]['value'], $db_items[$itemid]);
|
|
}
|
|
|
|
$macro_values[$key][$token] = $macro_value;
|
|
break;
|
|
}
|
|
|
|
if ($db_items[$itemid]['value_type'] != ITEM_VALUE_TYPE_LOG) {
|
|
continue;
|
|
}
|
|
|
|
switch ($data['macro']) {
|
|
case 'ITEM.LOG.DATE':
|
|
$macro_values[$key][$token] = date('Y.m.d', $history[$itemid][0]['timestamp']);
|
|
break;
|
|
|
|
case 'ITEM.LOG.TIME':
|
|
$macro_values[$key][$token] = date('H:i:s', $history[$itemid][0]['timestamp']);
|
|
break;
|
|
|
|
case 'ITEM.LOG.AGE':
|
|
$macro_values[$key][$token] = zbx_date2age($history[$itemid][0]['timestamp']);
|
|
break;
|
|
|
|
case 'ITEM.LOG.SOURCE':
|
|
$macro_values[$key][$token] = $history[$itemid][0]['source'];
|
|
break;
|
|
|
|
case 'ITEM.LOG.SEVERITY':
|
|
$macro_values[$key][$token]
|
|
= CSeverityHelper::getName((int) $history[$itemid][0]['severity']);
|
|
break;
|
|
|
|
case 'ITEM.LOG.NSEVERITY':
|
|
$macro_values[$key][$token] = $history[$itemid][0]['severity'];
|
|
break;
|
|
|
|
case 'ITEM.LOG.EVENTID':
|
|
$macro_values[$key][$token] = $history[$itemid][0]['logeventid'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Calculates regular expression substitution. Returns UNRESOLVED_MACRO_STRING in case of incorrect function
|
|
* parameters or regular expression.
|
|
*
|
|
* @param string $value [IN] The input value.
|
|
* @param array $parameters [IN] The function parameters.
|
|
* @param bool $insensitive [IN] Case insensitive match.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function macrofuncRegsub(string $value, array $parameters, bool $insensitive): string {
|
|
if (count($parameters) != 2) {
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
set_error_handler(function ($errno, $errstr) {});
|
|
$rc = preg_match('/'.$parameters[0].'/'.($insensitive ? 'i' : ''), $value, $matches);
|
|
restore_error_handler();
|
|
|
|
if ($rc === false) {
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
$macro_values = [];
|
|
foreach (self::getMacroPositions($parameters[1], ['replacements' => true]) as $macro) {
|
|
$macro_values[$macro] = array_key_exists($macro[1], $matches) ? $matches[$macro[1]] : '';
|
|
}
|
|
|
|
return strtr($parameters[1], $macro_values);
|
|
}
|
|
|
|
/**
|
|
* Calculates number formatting macro function. Returns UNRESOLVED_MACRO_STRING in case of incorrect function
|
|
* parameters or value. Formatting is not applied to integer values.
|
|
*
|
|
* @param string $value [IN] The input value.
|
|
* @param array $parameters [IN] The function parameters.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function macrofuncFmtnum(string $value, array $parameters): string {
|
|
if (count($parameters) != 1 || $parameters[0] == '') {
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
$parser = new CNumberParser(['with_float' => false]);
|
|
|
|
if ($parser->parse($value) == CParser::PARSE_SUCCESS) {
|
|
return $value;
|
|
}
|
|
|
|
$parser = new CNumberParser();
|
|
|
|
if ($parser->parse($value) != CParser::PARSE_SUCCESS) {
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
if (!ctype_digit($parameters[0]) || (int) $parameters[0] > 20) {
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
return sprintf('%.'.$parameters[0].'f', (float) $value);
|
|
}
|
|
|
|
/**
|
|
* Calculates macro function. Returns UNRESOLVED_MACRO_STRING in case of unsupported function.
|
|
*
|
|
* @param string $value [IN] The input value.
|
|
* @param array $macrofunc [IN]
|
|
* @param string $macrofunc['function'] [IN] The function name.
|
|
* @param array $macrofunc['parameters'] [IN] The function parameters.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function calcMacrofunc(string $value, array $macrofunc) {
|
|
switch ($macrofunc['function']) {
|
|
case 'regsub':
|
|
case 'iregsub':
|
|
return self::macrofuncRegsub($value, $macrofunc['parameters'], $macrofunc['function'] === 'iregsub');
|
|
|
|
case 'fmtnum':
|
|
return self::macrofuncFmtnum($value, $macrofunc['parameters']);
|
|
}
|
|
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
/**
|
|
* Calculates macro function for expression macros. Returns UNRESOLVED_MACRO_STRING in case of unsupported function.
|
|
*
|
|
* @param string $value [IN] The input value.
|
|
* @param array $macrofunc [IN]
|
|
* @param string $macrofunc['function'] [IN] The function name.
|
|
* @param array $macrofunc['parameters'] [IN] The function parameters.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function calcExpressionMacrofunc(string $value, array $macrofunc) {
|
|
switch ($macrofunc['function']) {
|
|
case 'fmtnum':
|
|
return self::macrofuncFmtnum($value, $macrofunc['parameters']);
|
|
}
|
|
|
|
return UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
/**
|
|
* Get item macros.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<functionid>]
|
|
* @param array $macros[<functionid>][<macro>] An array of the tokens.
|
|
* @param array $macro_values
|
|
* @param array $triggers
|
|
* @param array $options
|
|
* @param bool $options['events'] Resolve {ITEM.VALUE} macro using 'clock' and 'ns' fields.
|
|
* @param bool $options['html']
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getItemMacros(array $macros, array $macro_values, array $triggers = [], array $options = []) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$options += [
|
|
'events' => false,
|
|
'html' => false
|
|
];
|
|
|
|
$functions = DBfetchArray(DBselect(
|
|
'SELECT f.triggerid,f.functionid,i.itemid,i.name,i.value_type,i.units,i.valuemapid'.
|
|
' FROM functions f'.
|
|
' JOIN items i ON f.itemid=i.itemid'.
|
|
' JOIN hosts h ON i.hostid=h.hostid'.
|
|
' WHERE '.dbConditionInt('f.functionid', array_keys($macros))
|
|
));
|
|
|
|
$functions = self::getItemsValueMaps($functions);
|
|
|
|
// False passed to DBfetch to get data without null converted to 0, which is done by default.
|
|
foreach ($functions as $function) {
|
|
foreach ($macros[$function['functionid']] as $m => $tokens) {
|
|
$clock = null;
|
|
$value = null;
|
|
|
|
switch ($m) {
|
|
case 'ITEM.VALUE':
|
|
if ($options['events']) {
|
|
$trigger = $triggers[$function['triggerid']];
|
|
$history = Manager::History()->getValueAt($function, $trigger['clock'], $trigger['ns']);
|
|
|
|
if (is_array($history)) {
|
|
if (array_key_exists('clock', $history)) {
|
|
$clock = $history['clock'];
|
|
}
|
|
|
|
if (array_key_exists('value', $history)
|
|
&& $function['value_type'] != ITEM_VALUE_TYPE_BINARY) {
|
|
$value = $history['value'];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// break; is not missing here
|
|
|
|
case 'ITEM.LASTVALUE':
|
|
$history = Manager::History()->getLastValues([$function], 1, timeUnitToSeconds(
|
|
CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD)
|
|
));
|
|
|
|
if (array_key_exists($function['itemid'], $history)) {
|
|
$clock = $history[$function['itemid']][0]['clock'];
|
|
|
|
if ($function['value_type'] != ITEM_VALUE_TYPE_BINARY) {
|
|
$value = $history[$function['itemid']][0]['value'];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
foreach ($tokens as $token) {
|
|
if ($value !== null) {
|
|
$macro_value = array_key_exists('macrofunc', $token)
|
|
? self::calcMacrofunc($value, $token['macrofunc'])
|
|
: formatHistoryValue($value, $function);
|
|
}
|
|
else {
|
|
$macro_value = UNRESOLVED_MACRO_STRING;
|
|
}
|
|
|
|
if ($options['html']) {
|
|
$macro_value = str_replace(["\r\n", "\n"], [" "], $macro_value);
|
|
$hint_table = (new CTable())
|
|
->addClass('list-table')
|
|
->addRow([
|
|
new CCol($function['name']),
|
|
new CCol(
|
|
($clock !== null)
|
|
? zbx_date2str(DATE_TIME_FORMAT_SECONDS, $clock)
|
|
: UNRESOLVED_MACRO_STRING
|
|
),
|
|
new CCol($macro_value),
|
|
new CCol(
|
|
($function['value_type'] == ITEM_VALUE_TYPE_FLOAT
|
|
|| $function['value_type'] == ITEM_VALUE_TYPE_UINT64)
|
|
? new CLink(_('Graph'), (new CUrl('history.php'))
|
|
->setArgument('action', HISTORY_GRAPH)
|
|
->setArgument('itemids[]', $function['itemid'])
|
|
->getUrl()
|
|
)
|
|
: new CLink(_('History'), (new CUrl('history.php'))
|
|
->setArgument('action', HISTORY_VALUES)
|
|
->setArgument('itemids[]', $function['itemid'])
|
|
->getUrl()
|
|
)
|
|
)
|
|
]);
|
|
$macro_value = new CSpan([
|
|
(new CSpan())
|
|
->addClass('main-hint')
|
|
->setHint($hint_table),
|
|
(new CLinkAction($macro_value))
|
|
->addClass('hint-item')
|
|
->setAttribute('data-hintbox', '1')
|
|
]);
|
|
}
|
|
|
|
$macro_values[$function['triggerid']][$token['token']] = $macro_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
protected function getItemLogMacros(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$functions = DBfetchArray(DBselect(
|
|
'SELECT f.triggerid,f.functionid,i.itemid,i.value_type'.
|
|
' FROM functions f'.
|
|
' JOIN items i ON f.itemid=i.itemid'.
|
|
' JOIN hosts h ON i.hostid=h.hostid'.
|
|
' WHERE '.dbConditionInt('f.functionid', array_keys($macros)).
|
|
' AND i.value_type='.ITEM_VALUE_TYPE_LOG
|
|
));
|
|
|
|
if (!$functions) {
|
|
return $macro_values;
|
|
}
|
|
|
|
foreach ($functions as $function) {
|
|
foreach ($macros[$function['functionid']] as $m => $tokens) {
|
|
$value = UNRESOLVED_MACRO_STRING;
|
|
|
|
$history = Manager::History()->getLastValues([$function], 1, timeUnitToSeconds(
|
|
CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD)
|
|
));
|
|
|
|
if (!array_key_exists($function['itemid'], $history)) {
|
|
continue;
|
|
}
|
|
|
|
switch ($m) {
|
|
case 'ITEM.LOG.DATE':
|
|
$value = date('Y.m.d', $history[$function['itemid']][0]['timestamp']);
|
|
break;
|
|
case 'ITEM.LOG.TIME':
|
|
$value = date('H:i:s', $history[$function['itemid']][0]['timestamp']);
|
|
break;
|
|
case 'ITEM.LOG.AGE':
|
|
$value = zbx_date2age($history[$function['itemid']][0]['timestamp']);
|
|
break;
|
|
case 'ITEM.LOG.SOURCE':
|
|
$value = $history[$function['itemid']][0]['source'];
|
|
break;
|
|
case 'ITEM.LOG.SEVERITY':
|
|
$value = CSeverityHelper::getName((int) $history[$function['itemid']][0]['severity']);
|
|
break;
|
|
case 'ITEM.LOG.NSEVERITY':
|
|
$value = $history[$function['itemid']][0]['severity'];
|
|
break;
|
|
case 'ITEM.LOG.EVENTID':
|
|
$value = $history[$function['itemid']][0]['logeventid'];
|
|
break;
|
|
}
|
|
|
|
foreach ($tokens as $token) {
|
|
$macro_values[$function['triggerid']][$token['token']] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get host macros.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<functionid>]
|
|
* @param array $macros[<functionid>][<macro>] an array of the tokens
|
|
* @param array $macro_values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getHostMacros(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$result = DBselect(
|
|
'SELECT f.triggerid,f.functionid,h.hostid,h.host,h.name'.
|
|
' FROM functions f'.
|
|
' JOIN items i ON f.itemid=i.itemid'.
|
|
' JOIN hosts h ON i.hostid=h.hostid'.
|
|
' WHERE '.dbConditionInt('f.functionid', array_keys($macros))
|
|
);
|
|
|
|
while ($row = DBfetch($result)) {
|
|
foreach ($macros[$row['functionid']] as $macro => $tokens) {
|
|
switch ($macro) {
|
|
case 'HOST.ID':
|
|
$value = $row['hostid'];
|
|
break;
|
|
|
|
case 'HOSTNAME':
|
|
case 'HOST.HOST':
|
|
$value = $row['host'];
|
|
break;
|
|
|
|
case 'HOST.NAME':
|
|
$value = $row['name'];
|
|
break;
|
|
}
|
|
|
|
foreach ($tokens as $token) {
|
|
$macro_values[$row['triggerid']][$token['token']] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get expression macros like "{?avg(/host/key, 1d)}" or {{?min(/host/key, 1h)}.fmtnum(2)}.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<macro>]
|
|
* @param string $macros[<macro>]['function']
|
|
* @param string $macros[<macro>]['host']
|
|
* @param string $macros[<macro>]['key']
|
|
* @param string $macros[<macro>]['sec_num']
|
|
* @param array $macros[<macro>]['macrofunc'] (optional)
|
|
* @param string $macros[<macro>]['macrofunc']['function']
|
|
* @param array $macros[<macro>]['macrofunc']['parameters']
|
|
* @param array $macro_values
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getExpressionMacros(array $macros, array $macro_values) {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$function_data = [];
|
|
|
|
foreach ($macros as $macro => $data) {
|
|
$macro_data = ['macro' => $macro];
|
|
if (array_key_exists('macrofunc', $data)) {
|
|
$macro_data['macrofunc'] = $data['macrofunc'];
|
|
}
|
|
|
|
if ($data['function'] === 'last') {
|
|
$function_data['last'][$data['host']][$data['key']][] = $macro_data;
|
|
}
|
|
else {
|
|
$function_data['other'][$data['host']][$data['key']][$data['function']][$data['sec_num']][] =
|
|
$macro_data;
|
|
}
|
|
}
|
|
|
|
foreach ($function_data as $ftype => $hosts) {
|
|
foreach ($hosts as $host => $keys) {
|
|
if ($ftype === 'last') {
|
|
$db_items = API::Item()->get([
|
|
'output' => ['key_', 'value_type', 'units', 'lastvalue', 'lastclock'],
|
|
'selectValueMap' => ['mappings'],
|
|
'webitems' => true,
|
|
'filter' => [
|
|
'host' => $host,
|
|
'key_' => array_keys($keys)
|
|
]
|
|
]);
|
|
|
|
foreach ($db_items as $db_item) {
|
|
foreach ($keys[$db_item['key_']] as $macro_data) {
|
|
if ($db_item['lastclock'] && $db_item['value_type'] != ITEM_VALUE_TYPE_BINARY) {
|
|
$macro_values[$macro_data['macro']] = array_key_exists('macrofunc', $macro_data)
|
|
? self::calcExpressionMacrofunc($db_item['lastvalue'], $macro_data['macrofunc'])
|
|
: formatHistoryValue($db_item['lastvalue'], $db_item);
|
|
}
|
|
else {
|
|
$macro_values[$macro_data['macro']] = UNRESOLVED_MACRO_STRING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$db_items = API::Item()->get([
|
|
'output' => ['itemid', 'key_', 'value_type', 'units'],
|
|
'webitems' => true,
|
|
'filter' => [
|
|
'host' => $host,
|
|
'key_' => array_keys($keys)
|
|
]
|
|
]);
|
|
|
|
foreach ($db_items as $db_item) {
|
|
foreach ($keys[$db_item['key_']] as $function => $sec_nums) {
|
|
foreach ($sec_nums as $sec_num => $_macros) {
|
|
$value = getItemFunctionalValue($db_item, $function, $sec_num);
|
|
|
|
foreach ($_macros as $macro_data) {
|
|
if ($value !== null) {
|
|
$macro_values[$macro_data['macro']] = array_key_exists('macrofunc', $macro_data)
|
|
? self::calcExpressionMacrofunc($value, $macro_data['macrofunc'])
|
|
: convertUnits(['value' => $value, 'units' => $db_item['units']]);
|
|
}
|
|
else {
|
|
$macro_values[$macro_data['macro']] = UNRESOLVED_MACRO_STRING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get map macros.
|
|
*
|
|
* @param array $maps
|
|
* @param array $maps[<sysmapid>]
|
|
* @param array $maps[<sysmapid>][<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getMapMacros(array $maps, array $macro_values): array {
|
|
if (!$maps) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_maps = API::Map()->get([
|
|
'output' => ['name'],
|
|
'sysmapids' => array_keys($maps),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($maps as $mapid => $keys) {
|
|
if (!array_key_exists($mapid, $db_maps)) {
|
|
continue;
|
|
}
|
|
|
|
foreach (array_keys($keys) as $key) {
|
|
if (array_key_exists('{MAP.NAME}', $macro_values[$key])) {
|
|
$macro_values[$key]['{MAP.NAME}'] = $db_maps[$mapid]['name'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/*
|
|
* Resolve aggregated macros like {TRIGGER.EVENTS.*}, {TRIGGER(S).PROBLEM.*} and {TRIGGERS.(UN)ACK}.
|
|
*
|
|
* @param array $selement
|
|
* @param string $macro
|
|
*
|
|
* @return int
|
|
*/
|
|
private static function getTriggersMacroValue(array $selement, string $macro) {
|
|
switch ($macro) {
|
|
case '{TRIGGER.EVENTS.ACK}':
|
|
return get_events_unacknowledged($selement, null, null, true);
|
|
|
|
case '{TRIGGER.EVENTS.PROBLEM.ACK}':
|
|
return get_events_unacknowledged($selement, null, TRIGGER_VALUE_TRUE, true);
|
|
|
|
case '{TRIGGER.EVENTS.PROBLEM.UNACK}':
|
|
return get_events_unacknowledged($selement, null, TRIGGER_VALUE_TRUE);
|
|
|
|
case '{TRIGGER.EVENTS.UNACK}':
|
|
return get_events_unacknowledged($selement);
|
|
|
|
case '{TRIGGER.PROBLEM.EVENTS.PROBLEM.ACK}':
|
|
return get_events_unacknowledged($selement, TRIGGER_VALUE_TRUE, TRIGGER_VALUE_TRUE, true);
|
|
|
|
case '{TRIGGER.PROBLEM.EVENTS.PROBLEM.UNACK}':
|
|
return get_events_unacknowledged($selement, TRIGGER_VALUE_TRUE, TRIGGER_VALUE_TRUE);
|
|
|
|
case '{TRIGGERS.UNACK}':
|
|
return get_triggers_unacknowledged($selement);
|
|
|
|
case '{TRIGGERS.PROBLEM.UNACK}':
|
|
return get_triggers_unacknowledged($selement, true);
|
|
|
|
case '{TRIGGERS.ACK}':
|
|
return get_triggers_unacknowledged($selement, null, true);
|
|
|
|
case '{TRIGGERS.PROBLEM.ACK}':
|
|
return get_triggers_unacknowledged($selement, true, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get aggregated trigger macros.
|
|
*
|
|
* @param array $triggers
|
|
* @param array $triggers[<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
* @param array $selements
|
|
* @param array $selements[<key>][]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getAggrTriggerMacros(array $triggers, array $macro_values, array $selements): array {
|
|
if (!$triggers) {
|
|
return $macro_values;
|
|
}
|
|
|
|
foreach (array_keys($triggers) as $key) {
|
|
foreach ($macro_values[$key] as $macro => &$value) {
|
|
if (in_array($macro, self::aggr_triggers_macros)) {
|
|
$value = self::getTriggersMacroValue($selements[$key], $macro);
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get host macros.
|
|
*
|
|
* @param array $hosts
|
|
* @param array $hosts[<hostid>]
|
|
* @param array $hosts[<hostid>][<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getHostMacrosByHostId(array $hosts, array $macro_values): array {
|
|
if (!$hosts) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['host', 'name', 'description'],
|
|
'hostids' => array_keys($hosts),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$host_macros = ['{HOSTNAME}' => 'host', '{HOST.HOST}' => 'host', '{HOST.NAME}' => 'name',
|
|
'{HOST.DESCRIPTION}' => 'description'
|
|
];
|
|
|
|
foreach ($hosts as $hostid => $keys) {
|
|
if (!array_key_exists($hostid, $db_hosts)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($host_macros as $macro => $field) {
|
|
foreach (array_keys($keys) as $key) {
|
|
if (array_key_exists($macro, $macro_values[$key])) {
|
|
$macro_values[$key][$macro] = $db_hosts[$hostid][$field];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get interface macros by itemid.
|
|
*
|
|
* @param array $macros
|
|
* @param array $macros[<itemid>]
|
|
* @param array $macros[<itemid>][<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInterfaceMacrosByItemId(array $macros, array $macro_values): array {
|
|
if (!$macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_items = API::Item()->get([
|
|
'output' => ['hostid', 'interfaceid'],
|
|
'itemids' => array_keys($macros),
|
|
'webitems' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$interfaceids = [];
|
|
$hostids = [];
|
|
|
|
foreach ($db_items as $itemid => $db_item) {
|
|
if ($db_item['interfaceid'] != 0) {
|
|
// Collecting interface IDs for items with specific interface.
|
|
$interfaceids[$db_item['interfaceid']][] = $itemid;
|
|
}
|
|
else {
|
|
/*
|
|
* Collecting host IDs for items without interface. Macros for such items will resolve to either the
|
|
* Zabbix agent, SNMP, JMX or IPMI interface of the host in this order of priority or to 'UNKNOWN' if
|
|
* the host does not have any interface.
|
|
*/
|
|
$hostids[$db_item['hostid']][] = $itemid;
|
|
}
|
|
}
|
|
|
|
$db_interfaces = [];
|
|
|
|
if ($hostids) {
|
|
$host_interfaces = [];
|
|
|
|
$db_interfaces = API::HostInterface()->get([
|
|
'output' => ['hostid', 'type', 'main', 'useip', 'ip', 'dns', 'port'],
|
|
'hostids' => array_keys($hostids),
|
|
'filter' => ['main' => INTERFACE_PRIMARY],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
usort($db_interfaces, function ($a, $b) {
|
|
return self::interfacePriorities[$b['type']] <=> self::interfacePriorities[$a['type']];
|
|
});
|
|
|
|
/*
|
|
* Collecting host interfaces:
|
|
* - with highest priority for each host
|
|
* - with interface IDs contained in the $interfaceids array
|
|
*/
|
|
foreach ($db_interfaces as $interfaceid => $db_interface) {
|
|
if (array_key_exists($db_interface['hostid'], $hostids)) {
|
|
$host_interfaces[$db_interface['hostid']] = $interfaceid;
|
|
unset($hostids[$db_interface['hostid']]);
|
|
}
|
|
elseif (array_key_exists($interfaceid, $interfaceids)) {
|
|
unset($interfaceids[$interfaceid]);
|
|
}
|
|
else {
|
|
unset($db_interfaces[$interfaceid]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($interfaceids) {
|
|
$db_interfaces += API::HostInterface()->get([
|
|
'output' => ['hostid', 'type', 'main', 'useip', 'ip', 'dns', 'port'],
|
|
'interfaceids' => array_keys($interfaceids),
|
|
'preservekeys' => true
|
|
]);
|
|
}
|
|
|
|
$db_interfaces = CMacrosResolverHelper::resolveHostInterfaces($db_interfaces);
|
|
|
|
foreach ($db_interfaces as &$db_interface) {
|
|
$db_interface['conn'] = ($db_interface['useip'] == INTERFACE_USE_IP)
|
|
? $db_interface['ip']
|
|
: $db_interface['dns'];
|
|
}
|
|
unset($host_interface);
|
|
|
|
$interface_macros = ['{IPADDRESS}' => 'ip', '{HOST.IP}' => 'ip', '{HOST.DNS}' => 'dns',
|
|
'{HOST.CONN}' => 'conn', '{HOST.PORT}' => 'port'
|
|
];
|
|
|
|
foreach ($db_items as $itemid => $db_item) {
|
|
if ($db_item['interfaceid'] != 0) {
|
|
$interfaceid = $db_item['interfaceid'];
|
|
}
|
|
elseif (array_key_exists($db_item['hostid'], $host_interfaces)) {
|
|
$interfaceid = $host_interfaces[$db_item['hostid']];
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($interfaceid, $db_interfaces)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($interface_macros as $macro => $field) {
|
|
foreach ($macros[$itemid] as $key => $foo) {
|
|
if (array_key_exists($macro, $macro_values[$key])) {
|
|
$macro_values[$key][$macro] = $db_interfaces[$interfaceid][$field];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get interface macros.
|
|
*
|
|
* @param array $interfaces
|
|
* @param array $interfaces[<hostid>]
|
|
* @param array $interfaces[<hostid>][<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInterfaceMacrosByHostId(array $interfaces, array $macro_values): array {
|
|
if (!$interfaces) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$db_interfaces = API::HostInterface()->get([
|
|
'output' => ['hostid', 'type', 'useip', 'ip', 'dns'],
|
|
'hostids' => array_keys($interfaces),
|
|
'filter' => ['main' => INTERFACE_PRIMARY]
|
|
]);
|
|
|
|
usort($db_interfaces, function ($a, $b) {
|
|
return self::interfacePriorities[$b['type']] <=> self::interfacePriorities[$a['type']];
|
|
});
|
|
|
|
$host_interfaces = [];
|
|
|
|
foreach ($db_interfaces as $db_interface) {
|
|
if (!array_key_exists($db_interface['hostid'], $host_interfaces)) {
|
|
$host_interfaces[$db_interface['hostid']] = $db_interface;
|
|
}
|
|
}
|
|
|
|
foreach ($host_interfaces as &$host_interface) {
|
|
$host_interface['conn'] = ($host_interface['useip'] == INTERFACE_USE_IP)
|
|
? $host_interface['ip']
|
|
: $host_interface['dns'];
|
|
}
|
|
unset($host_interface);
|
|
|
|
$interface_macros = ['{IPADDRESS}' => 'ip', '{HOST.IP}' => 'ip', '{HOST.DNS}' => 'dns',
|
|
'{HOST.CONN}' => 'conn'
|
|
];
|
|
|
|
foreach ($interfaces as $hostid => $keys) {
|
|
if (!array_key_exists($hostid, $host_interfaces)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($interface_macros as $macro => $field) {
|
|
foreach (array_keys($keys) as $key) {
|
|
if (array_key_exists($macro, $macro_values[$key])) {
|
|
$macro_values[$key][$macro] = $host_interfaces[$hostid][$field];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Function returns array holding of inventory macros as a keys and corresponding database fields as value.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getSupportedHostInventoryMacrosMap(): array {
|
|
return [
|
|
'{INVENTORY.ALIAS}' => 'alias',
|
|
'{INVENTORY.ASSET.TAG}' => 'asset_tag',
|
|
'{INVENTORY.CHASSIS}' => 'chassis',
|
|
'{INVENTORY.CONTACT}' => 'contact',
|
|
'{PROFILE.CONTACT}' => 'contact', // deprecated
|
|
'{INVENTORY.CONTRACT.NUMBER}' => 'contract_number',
|
|
'{INVENTORY.DEPLOYMENT.STATUS}' => 'deployment_status',
|
|
'{INVENTORY.HARDWARE}' => 'hardware',
|
|
'{PROFILE.HARDWARE}' => 'hardware', // deprecated
|
|
'{INVENTORY.HARDWARE.FULL}' => 'hardware_full',
|
|
'{INVENTORY.HOST.NETMASK}' => 'host_netmask',
|
|
'{INVENTORY.HOST.NETWORKS}' => 'host_networks',
|
|
'{INVENTORY.HOST.ROUTER}' => 'host_router',
|
|
'{INVENTORY.HW.ARCH}' => 'hw_arch',
|
|
'{INVENTORY.HW.DATE.DECOMM}' => 'date_hw_decomm',
|
|
'{INVENTORY.HW.DATE.EXPIRY}' => 'date_hw_expiry',
|
|
'{INVENTORY.HW.DATE.INSTALL}' => 'date_hw_install',
|
|
'{INVENTORY.HW.DATE.PURCHASE}' => 'date_hw_purchase',
|
|
'{INVENTORY.INSTALLER.NAME}' => 'installer_name',
|
|
'{INVENTORY.LOCATION}' => 'location',
|
|
'{PROFILE.LOCATION}' => 'location', // deprecated
|
|
'{INVENTORY.LOCATION.LAT}' => 'location_lat',
|
|
'{INVENTORY.LOCATION.LON}' => 'location_lon',
|
|
'{INVENTORY.MACADDRESS.A}' => 'macaddress_a',
|
|
'{PROFILE.MACADDRESS}' => 'macaddress_a', // deprecated
|
|
'{INVENTORY.MACADDRESS.B}' => 'macaddress_b',
|
|
'{INVENTORY.MODEL}' => 'model',
|
|
'{INVENTORY.NAME}' => 'name',
|
|
'{PROFILE.NAME}' => 'name', // deprecated
|
|
'{INVENTORY.NOTES}' => 'notes',
|
|
'{PROFILE.NOTES}' => 'notes', // deprecated
|
|
'{INVENTORY.OOB.IP}' => 'oob_ip',
|
|
'{INVENTORY.OOB.NETMASK}' => 'oob_netmask',
|
|
'{INVENTORY.OOB.ROUTER}' => 'oob_router',
|
|
'{INVENTORY.OS}' => 'os',
|
|
'{PROFILE.OS}' => 'os', // deprecated
|
|
'{INVENTORY.OS.FULL}' => 'os_full',
|
|
'{INVENTORY.OS.SHORT}' => 'os_short',
|
|
'{INVENTORY.POC.PRIMARY.CELL}' => 'poc_1_cell',
|
|
'{INVENTORY.POC.PRIMARY.EMAIL}' => 'poc_1_email',
|
|
'{INVENTORY.POC.PRIMARY.NAME}' => 'poc_1_name',
|
|
'{INVENTORY.POC.PRIMARY.NOTES}' => 'poc_1_notes',
|
|
'{INVENTORY.POC.PRIMARY.PHONE.A}' => 'poc_1_phone_a',
|
|
'{INVENTORY.POC.PRIMARY.PHONE.B}' => 'poc_1_phone_b',
|
|
'{INVENTORY.POC.PRIMARY.SCREEN}' => 'poc_1_screen',
|
|
'{INVENTORY.POC.SECONDARY.CELL}' => 'poc_2_cell',
|
|
'{INVENTORY.POC.SECONDARY.EMAIL}' => 'poc_2_email',
|
|
'{INVENTORY.POC.SECONDARY.NAME}' => 'poc_2_name',
|
|
'{INVENTORY.POC.SECONDARY.NOTES}' => 'poc_2_notes',
|
|
'{INVENTORY.POC.SECONDARY.PHONE.A}' => 'poc_2_phone_a',
|
|
'{INVENTORY.POC.SECONDARY.PHONE.B}' => 'poc_2_phone_b',
|
|
'{INVENTORY.POC.SECONDARY.SCREEN}' => 'poc_2_screen',
|
|
'{INVENTORY.SERIALNO.A}' => 'serialno_a',
|
|
'{PROFILE.SERIALNO}' => 'serialno_a', // deprecated
|
|
'{INVENTORY.SERIALNO.B}' => 'serialno_b',
|
|
'{INVENTORY.SITE.ADDRESS.A}' => 'site_address_a',
|
|
'{INVENTORY.SITE.ADDRESS.B}' => 'site_address_b',
|
|
'{INVENTORY.SITE.ADDRESS.C}' => 'site_address_c',
|
|
'{INVENTORY.SITE.CITY}' => 'site_city',
|
|
'{INVENTORY.SITE.COUNTRY}' => 'site_country',
|
|
'{INVENTORY.SITE.NOTES}' => 'site_notes',
|
|
'{INVENTORY.SITE.RACK}' => 'site_rack',
|
|
'{INVENTORY.SITE.STATE}' => 'site_state',
|
|
'{INVENTORY.SITE.ZIP}' => 'site_zip',
|
|
'{INVENTORY.SOFTWARE}' => 'software',
|
|
'{PROFILE.SOFTWARE}' => 'software', // deprecated
|
|
'{INVENTORY.SOFTWARE.APP.A}' => 'software_app_a',
|
|
'{INVENTORY.SOFTWARE.APP.B}' => 'software_app_b',
|
|
'{INVENTORY.SOFTWARE.APP.C}' => 'software_app_c',
|
|
'{INVENTORY.SOFTWARE.APP.D}' => 'software_app_d',
|
|
'{INVENTORY.SOFTWARE.APP.E}' => 'software_app_e',
|
|
'{INVENTORY.SOFTWARE.FULL}' => 'software_full',
|
|
'{INVENTORY.TAG}' => 'tag',
|
|
'{PROFILE.TAG}' => 'tag', // deprecated
|
|
'{INVENTORY.TYPE}' => 'type',
|
|
'{PROFILE.DEVICETYPE}' => 'type', // deprecated
|
|
'{INVENTORY.TYPE.FULL}' => 'type_full',
|
|
'{INVENTORY.URL.A}' => 'url_a',
|
|
'{INVENTORY.URL.B}' => 'url_b',
|
|
'{INVENTORY.URL.C}' => 'url_c',
|
|
'{INVENTORY.VENDOR}' => 'vendor'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get inventory macros.
|
|
*
|
|
* @param array $hosts
|
|
* @param array $hosts[<hostid>]
|
|
* @param array $hosts[<hostid>][<key>]
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInventoryMacrosByHostId(array $hosts, array $macro_values): array {
|
|
if (!$hosts) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$inventory_macros = self::getSupportedHostInventoryMacrosMap();
|
|
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['inventory_mode'],
|
|
'selectInventory' => array_values($inventory_macros),
|
|
'hostids' => array_keys($hosts),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($hosts as $hostid => $keys) {
|
|
if (!array_key_exists($hostid, $db_hosts)
|
|
|| $db_hosts[$hostid]['inventory_mode'] == HOST_INVENTORY_DISABLED) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($inventory_macros as $macro => $field) {
|
|
foreach (array_keys($keys) as $key) {
|
|
if (array_key_exists($macro, $macro_values[$key])) {
|
|
$macro_values[$key][$macro] = $db_hosts[$hostid]['inventory'][$field];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get a list of hosts for each selected trigger, in the order in which they located in the expression.
|
|
* Returns an array of host IDs by trigger ID.
|
|
*
|
|
* @param array $triggerids
|
|
* @param bool $get_host_name Returns host names instead of host IDs.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getExpressionHosts(array $triggerids, bool $get_host_name = false): array {
|
|
if (!$triggerids) {
|
|
return [];
|
|
}
|
|
|
|
$db_triggers = API::Trigger()->get([
|
|
'output' => ['expression'],
|
|
'selectFunctions' => ['functionid', 'itemid'],
|
|
'selectItems' => ['itemid', 'hostid'],
|
|
'selectHosts' => $get_host_name ? ['hostid', 'host'] : null,
|
|
'triggerids' => $triggerids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$trigger_hosts_by_f_num = [];
|
|
$expression_parser = new CExpressionParser(['usermacros' => true, 'collapsed_expression' => true]);
|
|
|
|
foreach ($db_triggers as $triggerid => $db_trigger) {
|
|
if ($expression_parser->parse($db_trigger['expression']) != CParser::PARSE_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
$db_trigger['functions'] = array_column($db_trigger['functions'], 'itemid', 'functionid');
|
|
$db_trigger['items'] = array_column($db_trigger['items'], 'hostid', 'itemid');
|
|
if ($get_host_name) {
|
|
$db_trigger['hosts'] = array_column($db_trigger['hosts'], 'host', 'hostid');
|
|
}
|
|
$tokens = $expression_parser
|
|
->getResult()
|
|
->getTokensOfTypes([CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO]);
|
|
|
|
foreach ($tokens as $f_num => $token) {
|
|
$functionid = substr($token['match'], 1, -1); // strip curly braces
|
|
$itemid = $db_trigger['functions'][$functionid];
|
|
$hostid = $db_trigger['items'][$itemid];
|
|
$value = $get_host_name ? $db_trigger['hosts'][$hostid] : $hostid;
|
|
|
|
// Add host reference for macro without numeric index.
|
|
if ($f_num == 0) {
|
|
$trigger_hosts_by_f_num[$triggerid][0] = $value;
|
|
}
|
|
$trigger_hosts_by_f_num[$triggerid][$f_num + 1] = $value;
|
|
}
|
|
}
|
|
|
|
return $trigger_hosts_by_f_num;
|
|
}
|
|
|
|
/**
|
|
* Get host macros with references.
|
|
*
|
|
* @param array $hosts_n
|
|
* @param array $hosts_n[<triggerid>]
|
|
* @param array $hosts_n[<triggerid>][<key>]
|
|
* @param array $hosts_n[<triggerid>][<key>][<macro>]
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['macro'] Macro without curly braces and reference.
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['f_num'] Reference number.
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
* @param array $trigger_hosts_by_f_num
|
|
* @param array $trigger_hosts_by_f_num[<triggerid>] An array of host IDs.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getHostNMacros(array $hosts_n, array $macro_values,
|
|
array $trigger_hosts_by_f_num): array {
|
|
if (!$hosts_n) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$hostids = [];
|
|
|
|
foreach (array_intersect_key($trigger_hosts_by_f_num, $hosts_n) as $triggerid => $_hostids) {
|
|
$hostids += array_flip($_hostids);
|
|
}
|
|
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['host', 'name', 'description'],
|
|
'hostids' => array_keys($hostids),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$host_macros = ['HOST.ID' => null, 'HOSTNAME' => 'host', 'HOST.HOST' => 'host', 'HOST.NAME' => 'name',
|
|
'HOST.DESCRIPTION' => 'description'
|
|
];
|
|
|
|
foreach ($hosts_n as $triggerid => $keys) {
|
|
if (!array_key_exists($triggerid, $trigger_hosts_by_f_num)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $_macros) {
|
|
foreach ($macro_values[$key] as $macro => &$value) {
|
|
if (array_key_exists($macro, $_macros)) {
|
|
$data = $_macros[$macro];
|
|
|
|
if (!array_key_exists($data['macro'], $host_macros)) {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($data['f_num'], $trigger_hosts_by_f_num[$triggerid])) {
|
|
continue;
|
|
}
|
|
|
|
$hostid = $trigger_hosts_by_f_num[$triggerid][$data['f_num']];
|
|
|
|
if ($data['macro'] === 'HOST.ID') {
|
|
$value = $hostid;
|
|
}
|
|
elseif (array_key_exists($hostid, $db_hosts)) {
|
|
$value = $db_hosts[$hostid][$host_macros[$data['macro']]];
|
|
}
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get interface macros with references.
|
|
*
|
|
* @param array $hosts_n
|
|
* @param array $hosts_n[<triggerid>]
|
|
* @param array $hosts_n[<triggerid>][<key>]
|
|
* @param array $hosts_n[<triggerid>][<key>][<macro>]
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['macro'] Macro without curly braces and reference.
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['f_num'] Reference number.
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
* @param array $trigger_hosts_by_f_num
|
|
* @param array $trigger_hosts_by_f_num[<triggerid>] An array of host IDs.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInterfaceNMacros(array $hosts_n, array $macro_values,
|
|
array $trigger_hosts_by_f_num): array {
|
|
if (!$hosts_n) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$hostids = [];
|
|
|
|
foreach (array_intersect_key($trigger_hosts_by_f_num, $hosts_n) as $triggerid => $_hostids) {
|
|
$hostids += array_flip($_hostids);
|
|
}
|
|
|
|
$db_interfaces = API::HostInterface()->get([
|
|
'output' => ['hostid', 'type', 'useip', 'ip', 'dns'],
|
|
'hostids' => array_keys($hostids),
|
|
'filter' => ['main' => INTERFACE_PRIMARY]
|
|
]);
|
|
|
|
usort($db_interfaces, function ($a, $b) {
|
|
return self::interfacePriorities[$b['type']] <=> self::interfacePriorities[$a['type']];
|
|
});
|
|
|
|
$host_interfaces = [];
|
|
|
|
foreach ($db_interfaces as $db_interface) {
|
|
if (!array_key_exists($db_interface['hostid'], $host_interfaces)) {
|
|
$host_interfaces[$db_interface['hostid']] = $db_interface;
|
|
}
|
|
}
|
|
|
|
foreach ($host_interfaces as &$host_interface) {
|
|
$host_interface['conn'] = ($host_interface['useip'] == INTERFACE_USE_IP)
|
|
? $host_interface['ip']
|
|
: $host_interface['dns'];
|
|
}
|
|
unset($host_interface);
|
|
|
|
$interface_macros = ['IPADDRESS' => 'ip', 'HOST.IP' => 'ip', 'HOST.DNS' => 'dns', 'HOST.CONN' => 'conn'];
|
|
|
|
foreach ($hosts_n as $triggerid => $keys) {
|
|
if (!array_key_exists($triggerid, $trigger_hosts_by_f_num)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $_macros) {
|
|
foreach ($macro_values[$key] as $macro => &$value) {
|
|
if (array_key_exists($macro, $_macros)) {
|
|
$data = $_macros[$macro];
|
|
|
|
if (!array_key_exists($data['macro'], $interface_macros)) {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($data['f_num'], $trigger_hosts_by_f_num[$triggerid])) {
|
|
continue;
|
|
}
|
|
|
|
$hostid = $trigger_hosts_by_f_num[$triggerid][$data['f_num']];
|
|
|
|
if (array_key_exists($hostid, $host_interfaces)) {
|
|
$value = $host_interfaces[$hostid][$interface_macros[$data['macro']]];
|
|
}
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get inventory macros with references.
|
|
*
|
|
* @param array $hosts_n
|
|
* @param array $hosts_n[<triggerid>]
|
|
* @param array $hosts_n[<triggerid>][<key>]
|
|
* @param array $hosts_n[<triggerid>][<key>][<macro>]
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['macro'] Macro without curly braces and reference.
|
|
* @param string $hosts_n[<triggerid>][<key>][<macro>]['f_num'] Reference number.
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
* @param array $trigger_hosts_by_f_num
|
|
* @param array $trigger_hosts_by_f_num[<triggerid>] An array of host IDs.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInventoryNMacros(array $hosts_n, array $macro_values,
|
|
array $trigger_hosts_by_f_num): array {
|
|
if (!$hosts_n) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$hostids = [];
|
|
|
|
foreach (array_intersect_key($trigger_hosts_by_f_num, $hosts_n) as $triggerid => $_hostids) {
|
|
$hostids += array_flip($_hostids);
|
|
}
|
|
|
|
$inventory_macros = self::getSupportedHostInventoryMacrosMap();
|
|
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['inventory_mode'],
|
|
'selectInventory' => array_values($inventory_macros),
|
|
'hostids' => array_keys($hostids),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($hosts_n as $triggerid => $keys) {
|
|
if (!array_key_exists($triggerid, $trigger_hosts_by_f_num)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $_macros) {
|
|
foreach ($macro_values[$key] as $macro => &$value) {
|
|
if (array_key_exists($macro, $_macros)) {
|
|
$data = $_macros[$macro];
|
|
|
|
if (!array_key_exists('{'.$data['macro'].'}', $inventory_macros)) {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($data['f_num'], $trigger_hosts_by_f_num[$triggerid])) {
|
|
continue;
|
|
}
|
|
|
|
$hostid = $trigger_hosts_by_f_num[$triggerid][$data['f_num']];
|
|
|
|
if (array_key_exists($hostid, $db_hosts)
|
|
&& $db_hosts[$hostid]['inventory_mode'] != HOST_INVENTORY_DISABLED) {
|
|
$value = $db_hosts[$hostid]['inventory'][$inventory_macros['{'.$data['macro'].'}']];
|
|
}
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get expression macros with and without {HOST.HOST<1-9>} references.
|
|
*
|
|
* @param array $expr_macros_host_n
|
|
* @param array $expr_macros_host_n[<triggerid>]
|
|
* @param array $expr_macros_host_n[<triggerid>][<key>]
|
|
* @param array $expr_macros_host_n[<triggerid>][<key>][<macro>]
|
|
* @param string $expr_macros_host_n[<triggerid>][<key>][<macro>]['host']
|
|
* @param array $expr_macros_host
|
|
* @param array $expr_macros_host[<hostid>]
|
|
* @param array $expr_macros_host[<hostid>][<key>]
|
|
* @param array $expr_macros_host[<hostid>][<key>][<macro>]
|
|
* @param string $expr_macros_host[<hostid>][<key>][<macro>]['host']
|
|
* @param array $expr_macros
|
|
* @param array $expr_macros[<macro>]
|
|
* @param string $expr_macros[<macro>]['host']
|
|
* @param array $expr_macros[<macro>]['links']
|
|
* @param array $expr_macros[<macro>]['links'][<macro>] An array of keys.
|
|
* @param array $macro_values
|
|
* @param array $macro_values[<key>]
|
|
* @param array $macro_values[<key>][<macro>]
|
|
* @param array $trigger_hosts_by_f_num
|
|
* @param array $trigger_hosts_by_f_num[<triggerid>] An array of host IDs.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getExpressionNMacros(array $expr_macros_host_n, array $expr_macros_host,
|
|
array $expr_macros, array $macro_values): array {
|
|
if (!$expr_macros_host_n && !$expr_macros_host && !$expr_macros) {
|
|
return $macro_values;
|
|
}
|
|
|
|
$trigger_hosts_by_f_num = self::getExpressionHosts(array_keys($expr_macros_host_n), true);
|
|
$macro_parser = new CMacroParser(['macros' => ['{HOST.HOST}'], 'ref_type' => CMacroParser::REFERENCE_NUMERIC]);
|
|
|
|
foreach ($expr_macros_host_n as $triggerid => $keys) {
|
|
if (!array_key_exists($triggerid, $trigger_hosts_by_f_num)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $_macros) {
|
|
foreach ($_macros as $_macro => $data) {
|
|
if ($data['host'] === '') {
|
|
$reference = 0;
|
|
$pattern = '#//#';
|
|
}
|
|
else {
|
|
$macro_parser->parse($data['host']);
|
|
$reference = $macro_parser->getReference();
|
|
$pattern = '#/\{HOST\.HOST[1-9]?\}/#';
|
|
}
|
|
|
|
if (!array_key_exists($reference, $trigger_hosts_by_f_num[$triggerid])) {
|
|
continue;
|
|
}
|
|
|
|
$host = $trigger_hosts_by_f_num[$triggerid][$reference];
|
|
|
|
// Replace {HOST.HOST<1-9>} macro with real host name.
|
|
$macro = preg_replace($pattern, '/'.$host.'/', $_macro, 1);
|
|
|
|
if (!array_key_exists($macro, $expr_macros)) {
|
|
$expr_macros[$macro] = ['host' => $host] + $data;
|
|
}
|
|
$expr_macros[$macro]['links'][$_macro][] = $key;
|
|
}
|
|
}
|
|
}
|
|
|
|
$db_hosts = $expr_macros_host
|
|
? API::Host()->get([
|
|
'output' => ['host'],
|
|
'hostids' => array_keys($expr_macros_host),
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
foreach ($expr_macros_host as $hostid => $keys) {
|
|
if (!array_key_exists($hostid, $db_hosts)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($keys as $key => $_macros) {
|
|
foreach ($_macros as $_macro => $data) {
|
|
// Replace {HOST.HOST} macro with real host name.
|
|
$pattern = $data['host'] === '' ? '#//#' : '#/\{HOST\.HOST\}/#';
|
|
$macro = preg_replace($pattern, '/'.$db_hosts[$hostid]['host'].'/', $_macro, 1);
|
|
|
|
if (!array_key_exists($macro, $expr_macros)) {
|
|
$expr_macros[$macro] = ['host' => $db_hosts[$hostid]['host']] + $data;
|
|
}
|
|
$expr_macros[$macro]['links'][$_macro][] = $key;
|
|
}
|
|
}
|
|
}
|
|
|
|
$expr_macro_values = self::getExpressionMacros($expr_macros, []);
|
|
|
|
foreach ($expr_macros as $macro => $expr_macro) {
|
|
if (!array_key_exists($macro, $expr_macro_values)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($expr_macro['links'] as $_macro => $keys) {
|
|
foreach ($keys as $key) {
|
|
$macro_values[$key][$_macro] = $expr_macro_values[$macro];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Is type available.
|
|
*
|
|
* @param string $type
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function isTypeAvailable($type) {
|
|
return in_array($type, $this->configs[$this->config]['types']);
|
|
}
|
|
|
|
/**
|
|
* Get macros with values.
|
|
*
|
|
* @param array $data
|
|
* @param array $data[n]['hostids'] The list of host ids; [<hostid1>, ...].
|
|
* @param array $data[n]['macros'] The list of user macros to resolve, ['<usermacro1>' => null, ...].
|
|
* @param bool $unset_undefined Unset undefined macros.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getUserMacros(array $data, bool $unset_undefined = false) {
|
|
if (!$data) {
|
|
return $data;
|
|
}
|
|
|
|
// User macros.
|
|
$hostids = [];
|
|
foreach ($data as $element) {
|
|
foreach ($element['hostids'] as $hostid) {
|
|
$hostids[$hostid] = true;
|
|
}
|
|
}
|
|
|
|
$user_macro_parser = new CUserMacroParser();
|
|
|
|
/*
|
|
* @var array $host_templates
|
|
* @var array $host_templates[<hostid>] array of templates
|
|
*/
|
|
$host_templates = [];
|
|
|
|
/*
|
|
* @var array $host_macros
|
|
* @var array $host_macros[<hostid>]
|
|
* @var array $host_macros[<hostid>][<macro>] macro base without curly braces
|
|
* @var string $host_macros[<hostid>][<macro>]['value'] base macro value (without context and regex);
|
|
* can be null
|
|
* @var array $host_macros[<hostid>][<macro>]['contexts'] context values; ['<context1>' => '<value1>', ...]
|
|
* @var array $host_macros[<hostid>][<macro>]['regex'] regex values; ['<regex1>' => '<value1>', ...]
|
|
*/
|
|
$host_macros = [];
|
|
|
|
if ($hostids) {
|
|
do {
|
|
$hostids = array_keys($hostids);
|
|
|
|
$db_host_macros = API::UserMacro()->get([
|
|
'output' => ['macro', 'value', 'type', 'hostid'],
|
|
'hostids' => $hostids
|
|
]);
|
|
|
|
foreach ($db_host_macros as $db_host_macro) {
|
|
if ($user_macro_parser->parse($db_host_macro['macro']) != CParser::PARSE_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
$hostid = $db_host_macro['hostid'];
|
|
$macro = $user_macro_parser->getMacro();
|
|
$context = $user_macro_parser->getContext();
|
|
$regex = $user_macro_parser->getRegex();
|
|
$value = self::getMacroValue($db_host_macro);
|
|
|
|
if (!array_key_exists($hostid, $host_macros)) {
|
|
$host_macros[$hostid] = [];
|
|
}
|
|
|
|
if (!array_key_exists($macro, $host_macros[$hostid])) {
|
|
$host_macros[$hostid][$macro] = ['value' => null, 'contexts' => [], 'regex' => []];
|
|
}
|
|
|
|
if ($context === null && $regex === null) {
|
|
$host_macros[$hostid][$macro]['value'] = $value;
|
|
}
|
|
elseif ($regex !== null) {
|
|
$host_macros[$hostid][$macro]['regex'][$regex] = $value;
|
|
}
|
|
else {
|
|
$host_macros[$hostid][$macro]['contexts'][$context] = $value;
|
|
}
|
|
}
|
|
|
|
foreach ($hostids as $hostid) {
|
|
$host_templates[$hostid] = [];
|
|
}
|
|
|
|
$templateids = [];
|
|
$db_host_templates = DBselect(
|
|
'SELECT ht.hostid,ht.templateid'.
|
|
' FROM hosts_templates ht'.
|
|
' WHERE '.dbConditionInt('ht.hostid', $hostids)
|
|
);
|
|
while ($db_host_template = DBfetch($db_host_templates)) {
|
|
$host_templates[$db_host_template['hostid']][] = $db_host_template['templateid'];
|
|
$templateids[$db_host_template['templateid']] = true;
|
|
}
|
|
|
|
// only unprocessed templates will be populated
|
|
$hostids = [];
|
|
foreach (array_keys($templateids) as $templateid) {
|
|
if (!array_key_exists($templateid, $host_templates)) {
|
|
$hostids[$templateid] = true;
|
|
}
|
|
}
|
|
} while ($hostids);
|
|
}
|
|
|
|
// Reordering only regex array.
|
|
$host_macros = self::sortRegexHostMacros($host_macros);
|
|
|
|
$all_macros_resolved = true;
|
|
|
|
foreach ($data as &$element) {
|
|
$hostids = [];
|
|
foreach ($element['hostids'] as $hostid) {
|
|
$hostids[$hostid] = true;
|
|
}
|
|
|
|
$hostids = array_keys($hostids);
|
|
natsort($hostids);
|
|
|
|
foreach ($element['macros'] as $usermacro => &$value) {
|
|
if ($user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) {
|
|
$value = $this->getHostUserMacros($hostids, $user_macro_parser->getMacro(),
|
|
$user_macro_parser->getContext(), $host_templates, $host_macros
|
|
);
|
|
|
|
if ($value['value'] === null) {
|
|
$all_macros_resolved = false;
|
|
}
|
|
}
|
|
else {
|
|
// This macro cannot be resolved.
|
|
$value = ['value' => $usermacro, 'value_default' => null];
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
unset($element);
|
|
|
|
if (!$all_macros_resolved) {
|
|
// Global macros.
|
|
$db_global_macros = API::UserMacro()->get([
|
|
'output' => ['macro', 'value', 'type'],
|
|
'globalmacro' => true
|
|
]);
|
|
|
|
/*
|
|
* @var array $global_macros
|
|
* @var array $global_macros[<macro>] macro base without curly braces
|
|
* @var string $global_macros[<macro>]['value'] base macro value (without context and regex);
|
|
* can be null
|
|
* @var array $global_macros[<macro>]['contexts'] context values; ['<context1>' => '<value1>', ...]
|
|
* @var array $global_macros[<macro>]['regex'] regex values; ['<regex1>' => '<value1>', ...]
|
|
*/
|
|
$global_macros = [];
|
|
|
|
foreach ($db_global_macros as $db_global_macro) {
|
|
if ($user_macro_parser->parse($db_global_macro['macro']) == CParser::PARSE_SUCCESS) {
|
|
$macro = $user_macro_parser->getMacro();
|
|
$context = $user_macro_parser->getContext();
|
|
$regex = $user_macro_parser->getRegex();
|
|
$value = self::getMacroValue($db_global_macro);
|
|
|
|
if (!array_key_exists($macro, $global_macros)) {
|
|
$global_macros[$macro] = ['value' => null, 'contexts' => [], 'regex' => []];
|
|
}
|
|
|
|
if ($context === null && $regex === null) {
|
|
$global_macros[$macro]['value'] = $value;
|
|
}
|
|
elseif ($regex !== null) {
|
|
$global_macros[$macro]['regex'][$regex] = $value;
|
|
}
|
|
else {
|
|
$global_macros[$macro]['contexts'][$context] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reordering only regex array.
|
|
$global_macros = self::sortRegexGlobalMacros($global_macros);
|
|
|
|
foreach ($data as &$element) {
|
|
foreach ($element['macros'] as $usermacro => &$value) {
|
|
if ($value['value'] === null && $user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) {
|
|
$macro = $user_macro_parser->getMacro();
|
|
$context = $user_macro_parser->getContext();
|
|
|
|
if (array_key_exists($macro, $global_macros)) {
|
|
if ($context !== null && array_key_exists($context, $global_macros[$macro]['contexts'])) {
|
|
$value['value'] = $global_macros[$macro]['contexts'][$context];
|
|
}
|
|
elseif ($context !== null && count($global_macros[$macro]['regex'])) {
|
|
foreach ($global_macros[$macro]['regex'] as $regex => $val) {
|
|
if (preg_match('/'.strtr(trim($regex, '/'), ['/' => '\\/']).'/', $context) === 1) {
|
|
$value['value'] = $val;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($value['value'] === null && $global_macros[$macro]['value'] !== null) {
|
|
if ($context === null) {
|
|
$value['value'] = $global_macros[$macro]['value'];
|
|
}
|
|
elseif ($value['value_default'] === null) {
|
|
$value['value_default'] = $global_macros[$macro]['value'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
unset($element);
|
|
}
|
|
|
|
foreach ($data as $key => $element) {
|
|
foreach ($element['macros'] as $usermacro => $value) {
|
|
if ($value['value'] !== null) {
|
|
$data[$key]['macros'][$usermacro] = $value['value'];
|
|
}
|
|
elseif ($value['value_default'] !== null) {
|
|
$data[$key]['macros'][$usermacro] = $value['value_default'];
|
|
}
|
|
// Unresolved macro.
|
|
elseif ($unset_undefined) {
|
|
unset($data[$key]['macros'][$usermacro]);
|
|
}
|
|
else {
|
|
$data[$key]['macros'][$usermacro] = $usermacro;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get user macro from the requested hosts.
|
|
*
|
|
* Use the base value returned by host macro as default value when expanding expand global macro. This will ensure
|
|
* the following user macro resolving priority:
|
|
* 1) host/template context macro
|
|
* 2) global context macro
|
|
* 3) host/template base (default) macro
|
|
* 4) global base (default) macro
|
|
*
|
|
* @param array $hostids The sorted list of hosts where macros will be looked for (hostid => hostid)
|
|
* @param string $macro Macro base without curly braces, for example: SNMP_COMMUNITY
|
|
* @param string $context Macro context to resolve
|
|
* @param array $host_templates The list of linked templates (see getUserMacros() for more details)
|
|
* @param array $host_macros The list of macros on hosts (see getUserMacros() for more details)
|
|
* @param string $value_default Value
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getHostUserMacros(array $hostids, $macro, $context, array $host_templates, array $host_macros,
|
|
$value_default = null) {
|
|
foreach ($hostids as $hostid) {
|
|
if (array_key_exists($hostid, $host_macros) && array_key_exists($macro, $host_macros[$hostid])) {
|
|
// Searching context coincidence with macro contexts.
|
|
if ($context !== null && array_key_exists($context, $host_macros[$hostid][$macro]['contexts'])) {
|
|
return [
|
|
'value' => $host_macros[$hostid][$macro]['contexts'][$context],
|
|
'value_default' => $value_default
|
|
];
|
|
}
|
|
// Searching context coincidence, if regex array not empty.
|
|
elseif ($context !== null && count($host_macros[$hostid][$macro]['regex'])) {
|
|
foreach ($host_macros[$hostid][$macro]['regex'] as $regex => $val) {
|
|
if (preg_match('/'.strtr(trim($regex, '/'), ['/' => '\\/']).'/', $context) === 1) {
|
|
return [
|
|
'value' => $val,
|
|
'value_default' => $value_default
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($host_macros[$hostid][$macro]['value'] !== null) {
|
|
if ($context === null) {
|
|
return ['value' => $host_macros[$hostid][$macro]['value'], 'value_default' => $value_default];
|
|
}
|
|
elseif ($value_default === null) {
|
|
$value_default = $host_macros[$hostid][$macro]['value'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$host_templates) {
|
|
return ['value' => null, 'value_default' => $value_default];
|
|
}
|
|
|
|
$templateids = [];
|
|
|
|
foreach ($hostids as $hostid) {
|
|
if (array_key_exists($hostid, $host_templates)) {
|
|
foreach ($host_templates[$hostid] as $templateid) {
|
|
$templateids[$templateid] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($templateids) {
|
|
$templateids = array_keys($templateids);
|
|
natsort($templateids);
|
|
|
|
return $this->getHostUserMacros($templateids, $macro, $context, $host_templates, $host_macros,
|
|
$value_default
|
|
);
|
|
}
|
|
|
|
return ['value' => null, 'value_default' => $value_default];
|
|
}
|
|
|
|
/**
|
|
* Get and resolve user data macros like name, surname, username. Input array contains a collection of prepared
|
|
* and unresolved macros. Get data from API service, because direct requests to API do no have CWebUser data.
|
|
*
|
|
* Example input:
|
|
* array (
|
|
* 0 => array (
|
|
* '{USER.FULLNAME}' => '*UNKNOWN*',
|
|
* ),
|
|
* 1 => array (
|
|
* '{USER.NAME}' => '*UNKNOWN*',
|
|
* '{USER.SURNAME}' => '*UNKNOWN*',
|
|
* )
|
|
* )
|
|
*
|
|
* Output:
|
|
* array (
|
|
* 0 => array (
|
|
* '{USER.FULLNAME}' => 'Zabbix Administrator',
|
|
* ),
|
|
* 1 => array (
|
|
* '{USER.NAME}' => 'Zabbix',
|
|
* '{USER.SURNAME}' => 'Administrator',
|
|
* )
|
|
* )
|
|
*
|
|
* @param array $macro_values Array of macros to be replaced.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getUserDataMacros(array $macro_values): array {
|
|
foreach ($macro_values as &$macros) {
|
|
foreach ($macros as $macro => &$value) {
|
|
switch ($macro) {
|
|
case '{USER.ALIAS}': // Deprecated in version 5.4.
|
|
case '{USER.USERNAME}':
|
|
$value = CApiService::$userData['username'];
|
|
break;
|
|
|
|
case '{USER.FULLNAME}':
|
|
$fullname = [];
|
|
|
|
foreach (['name', 'surname'] as $field) {
|
|
if (CApiService::$userData[$field] !== '') {
|
|
$fullname[] = CApiService::$userData[$field];
|
|
}
|
|
}
|
|
|
|
$value = $fullname
|
|
? implode(' ', array_merge($fullname, ['('.CApiService::$userData['username'].')']))
|
|
: CApiService::$userData['username'];
|
|
break;
|
|
|
|
case '{USER.NAME}':
|
|
$value = CApiService::$userData['name'];
|
|
break;
|
|
|
|
case '{USER.SURNAME}':
|
|
$value = CApiService::$userData['surname'];
|
|
break;
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
unset($macros);
|
|
|
|
return $macro_values;
|
|
}
|
|
|
|
/**
|
|
* Get macro value refer by type.
|
|
*
|
|
* @param array $macro
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getMacroValue(array $macro): string {
|
|
return ($macro['type'] == ZBX_MACRO_TYPE_SECRET || $macro['type'] == ZBX_MACRO_TYPE_VAULT)
|
|
? ZBX_SECRET_MASK
|
|
: $macro['value'];
|
|
}
|
|
|
|
/**
|
|
* Sorting host macros.
|
|
*
|
|
* @param array $host_macros
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function sortRegexHostMacros(array $host_macros): array {
|
|
foreach ($host_macros as &$macros) {
|
|
foreach ($macros as &$value) {
|
|
$value['regex'] = self::sortRegex($value['regex']);
|
|
}
|
|
unset($value);
|
|
}
|
|
unset($macros);
|
|
|
|
return $host_macros;
|
|
}
|
|
|
|
/**
|
|
* Sorting global macros.
|
|
*
|
|
* @param array $global_macros
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function sortRegexGlobalMacros(array $global_macros): array {
|
|
foreach ($global_macros as &$value) {
|
|
$value['regex'] = self::sortRegex($value['regex']);
|
|
}
|
|
unset($value);
|
|
|
|
return $global_macros;
|
|
}
|
|
|
|
/**
|
|
* Sort regex.
|
|
*
|
|
* @param array $macros
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function sortRegex(array $macros): array {
|
|
$keys = array_keys($macros);
|
|
|
|
usort($keys, 'strcmp');
|
|
|
|
$new_array = [];
|
|
|
|
foreach($keys as $key) {
|
|
$new_array[$key] = $macros[$key];
|
|
}
|
|
|
|
return $new_array;
|
|
}
|
|
}
|