1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22/**
23 * Trigger expression converter from 2.2 to 2.4.
24 */
25class C20TriggerConverter extends CConverter {
26
27	/**
28	 * A parser for function macros.
29	 *
30	 * @var CFunctionMacroParser
31	 */
32	protected $function_macro_parser;
33
34	/**
35	 * A parser for LLD macros.
36	 *
37	 * @var CMacroParser
38	 */
39	protected $lld_macro_parser;
40
41	/**
42	 * An item key converter.
43	 *
44	 * @var C20ItemKeyConverter
45	 */
46	protected $itemKeyConverter;
47
48	public function __construct() {
49		$this->function_macro_parser = new CFunctionMacroParser();
50		$this->lld_macro_parser = new CLLDMacroParser();
51		$this->itemKeyConverter = new C20ItemKeyConverter();
52	}
53
54	/**
55	 * Convert a trigger expression from 2.2 format to 2.4.
56	 *
57	 * The method will replace old operators with their analogues: "&" with "and", "|" - "or" and "#" - "<>".
58	 *
59	 * @param string $expression
60	 *
61	 * @return string
62	 */
63	public function convert($expression) {
64		// find all the operators that need to be replaced
65		$found_operators = [];
66		for ($pos = 0; isset($expression[$pos]); $pos++) {
67			switch ($expression[$pos]) {
68				case '{':
69					// skip function macros
70					if ($this->function_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
71						$new_expression = '{'.
72							$this->function_macro_parser->getHost().':'.
73							$this->itemKeyConverter->convert($this->function_macro_parser->getItem()).'.'.
74							$this->function_macro_parser->getFunction().
75						'}';
76
77						$expression = substr_replace($expression, $new_expression, $pos,
78							$this->function_macro_parser->getLength()
79						);
80
81						$pos += strlen($new_expression) - 1;
82					}
83					else {
84						// if it's not a function macro, try to parse it as an LLD macro
85						if ($this->lld_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
86							$pos += $this->lld_macro_parser->getLength() - 1;
87						}
88					}
89					// otherwise just continue as is, other macros don't contain any of these characters
90					break;
91
92				case '&':
93				case '|':
94				case '#':
95					$found_operators[$pos] = $expression[$pos];
96					break;
97			}
98		}
99
100		// replace the operators
101		foreach (array_reverse($found_operators, true) as $pos => $operator) {
102			switch ($operator) {
103				case '&':
104					$expression = $this->replaceLogicalOperator($expression, 'and', $pos);
105
106					break;
107				case '|':
108					$expression = $this->replaceLogicalOperator($expression, 'or', $pos);
109
110					break;
111				case '#':
112					$expression = substr_replace($expression, '<>', $pos, 1);
113
114					break;
115			}
116		}
117
118		return $expression;
119	}
120
121	/**
122	 * Replaces an operator at the given position and removes extra spaces around it.
123	 *
124	 * @param string $expression
125	 * @param string $newOperator
126	 * @param int $pos
127	 *
128	 * @return string
129	 */
130	protected function replaceLogicalOperator($expression, $newOperator, $pos) {
131		$left = substr($expression, 0, $pos);
132		$right = substr($expression, $pos + 1);
133
134		return rtrim($left).' '.$newOperator.' '.ltrim($right);
135	}
136
137}
138