2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * A simple rules engine, that parses and executes the rules in advisory_rules.txt. Adjusted to phpMyAdmin
17 // HowTo: A simple Advisory system in 3 easy steps.
19 // Step 1: Get some variables to evaluate on
20 $this->variables
= array_merge(
21 PMA_DBI_fetch_result('SHOW GLOBAL STATUS', 0, 1),
22 PMA_DBI_fetch_result('SHOW GLOBAL VARIABLES', 0, 1)
25 $this->variables
= array_merge($this->variables
,
27 "SELECT concat('Com_', variable_name), variable_value
28 FROM data_dictionary.GLOBAL_STATEMENTS", 0, 1));
30 // Add total memory to variables as well
31 require_once 'libraries/sysinfo.lib.php';
32 $sysinfo = getSysInfo();
33 $memory = $sysinfo->memory();
34 $this->variables
['system_memory'] = $memory['MemTotal'];
36 // Step 2: Read and parse the list of rules
37 $this->parseResult
= $this->parseRulesFile();
38 // Step 3: Feed the variables to the rules and let them fire. Sets $runResult
41 return array('parse' => array('errors' => $this->parseResult
['errors']), 'run' => $this->runResult
);
45 $this->runResult
= array(
47 'notfired' => array(),
48 'unchecked'=> array(),
52 foreach ($this->parseResult
['rules'] as $rule) {
53 $this->variables
['value'] = 0;
56 if (isset($rule['precondition'])) {
58 $precond = $this->ruleExprEvaluate($rule['precondition']);
59 } catch (Exception
$e) {
60 $this->runResult
['errors'][] = 'Failed evaluating precondition for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage();
66 $this->addRule('unchecked', $rule);
69 $value = $this->ruleExprEvaluate($rule['formula']);
70 } catch(Exception
$e) {
71 $this->runResult
['errors'][] = 'Failed calculating value for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage();
75 $this->variables
['value'] = $value;
78 if ($this->ruleExprEvaluate($rule['test'])) {
79 $this->addRule('fired', $rule);
81 $this->addRule('notfired', $rule);
83 } catch(Exception
$e) {
84 $this->runResult
['errors'][] = 'Failed running test for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage();
93 * Escapes percent string to be used in format string.
98 function escapePercent($str)
100 return preg_replace('/%( |,|\.|$)/','%%\1', $str);
104 * Wrapper function for translating.
107 * @param mixed $param
110 function translate($str, $param = null)
112 if (is_null($param)) {
113 return sprintf(_gettext(Advisor
::escapePercent($str)));
115 $printf = 'sprintf("' . _gettext(Advisor
::escapePercent($str)) . '",';
116 return $this->ruleExprEvaluate(
117 $printf . $param . ')',
124 * Splits justification to text and formula.
126 * @param string $rule
129 function splitJustification($rule)
131 $jst = preg_split('/\s*\|\s*/', $rule['justification'], 2);
132 if (count($jst) > 1) {
133 return array($jst[0], $jst[1]);
135 return array($rule['justification']);
138 // Adds a rule to the result list
139 function addRule($type, $rule)
144 $jst = Advisor
::splitJustification($rule);
145 if (count($jst) > 1) {
148 $str = $this->translate($jst[0], $jst[1]);
149 } catch (Exception
$e) {
150 $this->runResult
['errors'][] = sprintf(
151 __('Failed formatting string for rule \'%s\'. PHP threw following error: %s'),
158 $rule['justification'] = $str;
160 $rule['justification'] = $this->translate($rule['justification']);
162 $rule['name'] = $this->translate($rule['name']);
163 $rule['issue'] = $this->translate($rule['issue']);
165 // Replaces {server_variable} with 'server_variable' linking to server_variables.php
166 $rule['recommendation'] = preg_replace(
167 '/\{([a-z_0-9]+)\}/Ui',
168 '<a href="server_variables.php?' . PMA_generate_common_url() . '#filter=\1">\1</a>',
169 $this->translate($rule['recommendation']));
171 // Replaces external Links with PMA_linkURL() generated links
172 $rule['recommendation'] = preg_replace(
173 '#href=("|\')(https?://[^\1]+)\1#ie',
174 '\'href="\' . PMA_linkURL("\2") . \'"\'',
175 $rule['recommendation']
180 $this->runResult
[$type][] = $rule;
183 private function ruleExprEvaluate_var1($matches)
185 // '/fired\s*\(\s*(\'|")(.*)\1\s*\)/Uie'
186 return '1'; //isset($this->runResult[\'fired\']
189 private function ruleExprEvaluate_var2($matches)
192 return isset($this->variables
[$matches[1]])
193 ?
(is_numeric($this->variables
[$matches[1]])
194 ?
$this->variables
[$matches[1]]
195 : '"'.$this->variables
[$matches[1]].'"')
199 // Runs a code expression, replacing variable names with their respective values
200 // ignoreUntil: if > 0, it doesn't replace any variables until that string position, but still evaluates the whole expr
201 function ruleExprEvaluate($expr, $ignoreUntil = 0)
203 if ($ignoreUntil > 0) {
204 $exprIgnore = substr($expr,0,$ignoreUntil);
205 $expr = substr($expr,$ignoreUntil);
207 $expr = preg_replace_callback('/fired\s*\(\s*(\'|")(.*)\1\s*\)/Ui', array($this, 'ruleExprEvaluate_var1'), $expr);
208 $expr = preg_replace_callback('/\b(\w+)\b/', array($this, 'ruleExprEvaluate_var2'), $expr);
209 if ($ignoreUntil > 0) {
210 $expr = $exprIgnore . $expr;
216 eval('$value = '.$expr.';');
217 $err = ob_get_contents();
220 throw new Exception(strip_tags($err) . '<br />Executed code: $value = '.$expr.';');
225 // Reads the rule file into an array, throwing errors messages on syntax errors
226 function parseRulesFile()
228 $file = file('libraries/advisory_rules.txt');
231 $ruleSyntax = array('name','formula','test','issue','recommendation','justification');
232 $numRules = count($ruleSyntax);
233 $numLines = count($file);
237 for ($i = 0; $i<$numLines; $i++
) {
239 if ($line[0] == '#' ||
$line[0] == "\n") {
244 if (substr($line, 0, 4) == 'rule') {
246 $errors[] = 'Invalid rule declaration on line '.($i+
1). ', expected line '.$ruleSyntax[$ruleLine++
].' of previous rule' ;
249 if (preg_match("/rule\s'(.*)'( \[(.*)\])?$/",$line,$match)) {
252 $rules[$j] = array( 'name' => $match[1]);
253 if(isset($match[3])) $rules[$j]['precondition'] = $match[3];
255 $errors[] = 'Invalid rule declaration on line '.($i+
1);
259 if ($ruleLine == -1) {
260 $errors[] = 'Unexpected characters on line '.($i+
1);
264 // Reading rule lines
266 if (!isset($line[0])) {
267 continue; // Empty lines are ok
269 // Non tabbed lines are not
270 if ($line[0] != "\t") {
271 $errors[] = 'Unexpected character on line '.($i+
1).'
272 . Expected tab, but found \''.$line[0].'\'';
275 $rules[$j][$ruleSyntax[$ruleLine++
]] = chop(substr($line,1));
279 if ($ruleLine == $numRules) {
284 return array('rules' => $rules, 'errors' => $errors);
288 function PMA_bytime($num, $precision)
291 if ($num >= 1) { # per second
293 } elseif ($num*60 >= 1) { # per minute
296 } elseif ($num*60*60 >=1 ) { # per hour
300 $num = $num*60*60*24;
304 $num = round($num, $precision);
307 $num = '<'.pow(10,-$precision);