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 * A parser for scheduling intervals. 24 */ 25class CSchedulingIntervalParser extends CParser { 26 27 private $options = [ 28 'usermacros' => false, 29 'lldmacros' => false 30 ]; 31 32 private $user_macro_parser; 33 private $lld_macro_parser; 34 private $lld_macro_function_parser; 35 36 public function __construct($options = []) { 37 if (array_key_exists('usermacros', $options)) { 38 $this->options['usermacros'] = $options['usermacros']; 39 } 40 if (array_key_exists('lldmacros', $options)) { 41 $this->options['lldmacros'] = $options['lldmacros']; 42 } 43 44 if ($this->options['usermacros']) { 45 $this->user_macro_parser = new CUserMacroParser(); 46 } 47 if ($this->options['lldmacros']) { 48 $this->lld_macro_parser = new CLLDMacroParser(); 49 $this->lld_macro_function_parser = new CLLDMacroFunctionParser(); 50 } 51 } 52 53 /** 54 * Parse the given scheduled interval. 55 * 56 * @param string $source Source string that needs to be parsed. 57 * @param int $pos Position offset. 58 */ 59 public function parse($source, $pos = 0) { 60 $this->length = 0; 61 $this->match = ''; 62 63 $p = $pos; 64 65 if ($this->options['usermacros'] && $this->user_macro_parser->parse($source, $p) != self::PARSE_FAIL) { 66 $p += $this->user_macro_parser->getLength(); 67 } 68 elseif ($this->options['lldmacros'] && $this->lld_macro_parser->parse($source, $p) != self::PARSE_FAIL) { 69 $p += $this->lld_macro_parser->getLength(); 70 } 71 elseif ($this->options['lldmacros'] 72 && $this->lld_macro_function_parser->parse($source, $p) != self::PARSE_FAIL) { 73 $p += $this->lld_macro_function_parser->getLength(); 74 } 75 elseif (!self::parseIntervals($source, $p)) { 76 return self::PARSE_FAIL; 77 } 78 79 $this->length = $p - $pos; 80 $this->match = substr($source, $pos, $this->length); 81 82 return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS; 83 } 84 85 /** 86 * Parse multiple intervals. 87 * 88 * @param string $source 89 * @param int $pos 90 * 91 * @return bool 92 */ 93 private static function parseIntervals($source, &$pos) { 94 $p = $pos; 95 96 $precedence = 0; 97 $prefixes = [ 98 0 => ['prefix' => 'md', 'min' => 1, 'max' => 31], 99 1 => ['prefix' => 'wd', 'min' => 1, 'max' => 7], 100 2 => ['prefix' => 'h', 'min' => 0, 'max' => 23], 101 3 => ['prefix' => 'm', 'min' => 0, 'max' => 59], 102 4 => ['prefix' => 's', 'min' => 0, 'max' => 59] 103 ]; 104 105 while (isset($source[$p])) { 106 for ($i = $precedence; $i < count($prefixes); $i++) { 107 $prefix = $prefixes[$i]; 108 109 if (self::parseInterval($source, $p, $prefix['prefix'], $prefix['min'], $prefix['max'])) { 110 $precedence = $i + 1; 111 continue 2; 112 } 113 } 114 break; 115 } 116 117 $ret = ($p != $pos); 118 $pos = $p; 119 120 return $ret; 121 } 122 123 /** 124 * Parse single interval. 125 * 126 * @param string $source 127 * @param int $pos 128 * @param string $prefix 129 * @param int $min 130 * @param int $max 131 * 132 * @return bool 133 */ 134 private static function parseInterval($source, &$pos, $prefix, $min, $max) { 135 $p = $pos; 136 $len = strlen($prefix); 137 138 if (substr($source, $p, $len) !== $prefix) { 139 return false; 140 } 141 $p += $len; 142 143 if (!self::parseFilter($source, $p, $min, $max)) { 144 return false; 145 } 146 147 $pos = $p; 148 149 return true; 150 } 151 152 /** 153 * Detect and move position at the end of filter. 154 */ 155 private static function parseFilter($source, &$pos, $min, $max) { 156 $p = $pos; 157 158 $max_digits = strlen((string) $max); 159 $pattern_range = '(?<from>[0-9]{1,'.$max_digits.'})(-(?P<to>[0-9]{1,'.$max_digits.'}))?'; 160 $pattern_step = '\/(?P<step>[0-9]{1,'.$max_digits.'})'; 161 $delimiter = ''; 162 163 while (isset($source[$p])) { 164 $len = 0; 165 166 if (preg_match('/^'.$delimiter.$pattern_range.'/', substr($source, $p), $matches)) { 167 $from = $matches['from']; 168 $to = (array_key_exists('to', $matches) && $matches['to'] !== '') ? $matches['to'] : $from; 169 170 if ($from < $min || $to > $max || $from > $to) { 171 break; 172 } 173 174 $len += strlen($matches[0]); 175 $delimiter = ''; 176 } 177 else { 178 $from = $min; 179 $to = $max; 180 } 181 182 if (preg_match('/^'.$delimiter.$pattern_step.'/', substr($source, $p + $len), $matches)) { 183 if ($matches['step'] >= 1 && $matches['step'] <= $to - $from) { 184 $len += strlen($matches[0]); 185 } 186 } 187 188 if ($len == 0) { 189 break; 190 } 191 192 $p += $len; 193 $delimiter = ','; 194 } 195 196 $ret = ($p != $pos); 197 $pos = $p; 198 199 return $ret; 200 } 201} 202