Translation update done using Pootle.
[phpmyadmin/madhuracj.git] / libraries / Advisor.class.php
blobbf4e404e5105c99497a725b2c50897ef4a988a9f
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * A simple rules engine, that parses and executes the rules in advisory_rules.txt. Adjusted to phpMyAdmin
7 * @package phpMyAdmin
8 */
10 class Advisor
12 var $variables;
13 var $parseResult;
14 var $runResult;
16 function run() {
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)
24 if (PMA_DRIZZLE) {
25 $this->variables = array_merge($this->variables,
26 PMA_DBI_fetch_result(
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
39 $this->runRules();
41 return array('parse' => array('errors' => $this->parseResult['errors']), 'run' => $this->runResult);
44 function runRules() {
45 $this->runResult = array(
46 'fired' => array(),
47 'notfired' => array(),
48 'unchecked'=> array(),
49 'errors' => array()
52 foreach ($this->parseResult['rules'] as $rule) {
53 $this->variables['value'] = 0;
54 $precond = true;
56 if (isset($rule['precondition'])) {
57 try {
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();
61 continue;
65 if (! $precond) {
66 $this->addRule('unchecked', $rule);
67 } else {
68 try {
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();
72 continue;
75 $this->variables['value'] = $value;
77 try {
78 if ($this->ruleExprEvaluate($rule['test'])) {
79 $this->addRule('fired', $rule);
80 } else {
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();
89 return true;
92 /**
93 * Escapes percent string to be used in format string.
95 * @param string $str
96 * @return string
98 function escapePercent($str)
100 return preg_replace('/%( |,|\.|$)/','%%\1', $str);
104 * Wrapper function for translating.
106 * @param string $str
107 * @param mixed $param
108 * @return string
110 function translate($str, $param = null)
112 if (is_null($param)) {
113 return sprintf(_gettext(Advisor::escapePercent($str)));
114 } else {
115 $printf = 'sprintf("' . _gettext(Advisor::escapePercent($str)) . '",';
116 return $this->ruleExprEvaluate(
117 $printf . $param . ')',
118 strlen($printf)
124 * Splits justification to text and formula.
126 * @param string $rule
127 * @return array
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)
141 switch($type) {
142 case 'notfired':
143 case 'fired':
144 $jst = Advisor::splitJustification($rule);
145 if (count($jst) > 1) {
146 try {
147 /* Translate */
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'),
152 $rule['name'],
153 $e->getMessage()
155 return;
158 $rule['justification'] = $str;
159 } else {
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']
177 break;
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)
191 // '/\b(\w+)\b/e'
192 return isset($this->variables[$matches[1]])
193 ? (is_numeric($this->variables[$matches[1]])
194 ? $this->variables[$matches[1]]
195 : '"'.$this->variables[$matches[1]].'"')
196 : $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;
212 $value = 0;
213 $err = 0;
215 ob_start();
216 eval('$value = '.$expr.';');
217 $err = ob_get_contents();
218 ob_end_clean();
219 if ($err) {
220 throw new Exception(strip_tags($err) . '<br />Executed code: $value = '.$expr.';');
222 return $value;
225 // Reads the rule file into an array, throwing errors messages on syntax errors
226 function parseRulesFile()
228 $file = file('libraries/advisory_rules.txt');
229 $errors = array();
230 $rules = array();
231 $ruleSyntax = array('name','formula','test','issue','recommendation','justification');
232 $numRules = count($ruleSyntax);
233 $numLines = count($file);
234 $j = -1;
235 $ruleLine = -1;
237 for ($i = 0; $i<$numLines; $i++) {
238 $line = $file[$i];
239 if ($line[0] == '#' || $line[0] == "\n") {
240 continue;
243 // Reading new rule
244 if (substr($line, 0, 4) == 'rule') {
245 if ($ruleLine > 0) {
246 $errors[] = 'Invalid rule declaration on line '.($i+1). ', expected line '.$ruleSyntax[$ruleLine++].' of previous rule' ;
247 continue;
249 if (preg_match("/rule\s'(.*)'( \[(.*)\])?$/",$line,$match)) {
250 $ruleLine = 1;
251 $j++;
252 $rules[$j] = array( 'name' => $match[1]);
253 if(isset($match[3])) $rules[$j]['precondition'] = $match[3];
254 } else {
255 $errors[] = 'Invalid rule declaration on line '.($i+1);
257 continue;
258 } else {
259 if ($ruleLine == -1) {
260 $errors[] = 'Unexpected characters on line '.($i+1);
264 // Reading rule lines
265 if ($ruleLine > 0) {
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].'\'';
273 continue;
275 $rules[$j][$ruleSyntax[$ruleLine++]] = chop(substr($line,1));
278 // Rule complete
279 if ($ruleLine == $numRules) {
280 $ruleLine = -1;
284 return array('rules' => $rules, 'errors' => $errors);
288 function PMA_bytime($num, $precision)
290 $per = '';
291 if ($num >= 1) { # per second
292 $per = "per second";
293 } elseif ($num*60 >= 1) { # per minute
294 $num = $num*60;
295 $per = "per minute";
296 } elseif ($num*60*60 >=1 ) { # per hour
297 $num = $num*60*60;
298 $per = "per hour";
299 } else {
300 $num = $num*60*60*24;
301 $per = "per day";
304 $num = round($num, $precision);
306 if ($num == 0) {
307 $num = '<'.pow(10,-$precision);
310 return "$num $per";