variables = array_merge( PMA_DBI_fetch_result('SHOW GLOBAL STATUS', 0, 1), PMA_DBI_fetch_result('SHOW GLOBAL VARIABLES', 0, 1) ); if (PMA_DRIZZLE) { $this->variables = array_merge( $this->variables, PMA_DBI_fetch_result( "SELECT concat('Com_', variable_name), variable_value FROM data_dictionary.GLOBAL_STATEMENTS", 0, 1 ) ); } // Add total memory to variables as well include_once 'libraries/sysinfo.lib.php'; $sysinfo = getSysInfo(); $memory = $sysinfo->memory(); $this->variables['system_memory'] = $memory['MemTotal']; // Step 2: Read and parse the list of rules $this->parseResult = $this->parseRulesFile(); // Step 3: Feed the variables to the rules and let them fire. Sets // $runResult $this->runRules(); return array( 'parse' => array('errors' => $this->parseResult['errors']), 'run' => $this->runResult ); } /** * Stores current error in run results. * * @param string $description description of an error. * @param object $exception exception raised * * @return void */ function storeError($description, $exception) { $this->runResult['errors'][] = $description . ' ' . sprintf(__('PHP threw following error: %s'), $exception->getMessage()); } function runRules() { $this->runResult = array( 'fired' => array(), 'notfired' => array(), 'unchecked'=> array(), 'errors' => array() ); foreach ($this->parseResult['rules'] as $rule) { $this->variables['value'] = 0; $precond = true; if (isset($rule['precondition'])) { try { $precond = $this->ruleExprEvaluate($rule['precondition']); } catch (Exception $e) { $this->storeError( sprintf( __('Failed evaluating precondition for rule \'%s\''), $rule['name'] ), $e ); continue; } } if (! $precond) { $this->addRule('unchecked', $rule); } else { try { $value = $this->ruleExprEvaluate($rule['formula']); } catch(Exception $e) { $this->storeError( sprintf( __('Failed calculating value for rule \'%s\''), $rule['name'] ), $e ); continue; } $this->variables['value'] = $value; try { if ($this->ruleExprEvaluate($rule['test'])) { $this->addRule('fired', $rule); } else { $this->addRule('notfired', $rule); } } catch(Exception $e) { $this->storeError( sprintf( __('Failed running test for rule \'%s\''), $rule['name'] ), $e ); } } } return true; } /** * Escapes percent string to be used in format string. * * @param string $str string to escape * * @return string */ static function escapePercent($str) { return preg_replace('/%( |,|\.|$|\(|\)|<|>)/', '%%\1', $str); } /** * Wrapper function for translating. * * @param string $str the string * @param mixed $param the parameters * * @return string */ function translate($str, $param = null) { if (is_null($param)) { return sprintf(_gettext(Advisor::escapePercent($str))); } else { $printf = 'sprintf("' . _gettext(Advisor::escapePercent($str)) . '",'; return $this->ruleExprEvaluate( $printf . $param . ')', strlen($printf) ); } } /** * Splits justification to text and formula. * * @param string $rule the rule * * @return array */ static function splitJustification($rule) { $jst = preg_split('/\s*\|\s*/', $rule['justification'], 2); if (count($jst) > 1) { return array($jst[0], $jst[1]); } return array($rule['justification']); } /** * Adds a rule to the result list * * @param string $type type of rule * @param array $rule rule itslef * * @return void */ function addRule($type, $rule) { switch($type) { case 'notfired': case 'fired': $jst = Advisor::splitJustification($rule); if (count($jst) > 1) { try { /* Translate */ $str = $this->translate($jst[0], $jst[1]); } catch (Exception $e) { $this->storeError( sprintf( __('Failed formatting string for rule \'%s\'.'), $rule['name'] ), $e ); return; } $rule['justification'] = $str; } else { $rule['justification'] = $this->translate($rule['justification']); } $rule['id'] = $rule['name']; $rule['name'] = $this->translate($rule['name']); $rule['issue'] = $this->translate($rule['issue']); // Replaces {server_variable} with 'server_variable' // linking to server_variables.php $rule['recommendation'] = preg_replace( '/\{([a-z_0-9]+)\}/Ui', '\1', $this->translate($rule['recommendation']) ); // Replaces external Links with PMA_linkURL() generated links $rule['recommendation'] = preg_replace( '#href=("|\')(https?://[^\1]+)\1#ie', '\'href="\' . PMA_linkURL("\2") . \'"\'', $rule['recommendation'] ); break; } $this->runResult[$type][] = $rule; } /** * Callback for evaluating fired() condition. * * @param array $matches List of matched elements form preg_replace_callback * * @return Replacement value */ private function _ruleExprEvaluateFired($matches) { // No list of fired rules if (!isset($this->runResult['fired'])) { return '0'; } // Did matching rule fire? foreach ($this->runResult['fired'] as $rule) { if ($rule['id'] == $matches[2]) { return '1'; } } return '0'; } /** * Callback for evaluating variables in expression. * * @param array $matches List of matched elements form preg_replace_callback * * @return Replacement value */ private function _ruleExprEvaluateVariable($matches) { return isset($this->variables[$matches[1]]) ? (is_numeric($this->variables[$matches[1]]) ? $this->variables[$matches[1]] : '"'.$this->variables[$matches[1]].'"') : $matches[1]; } /** * Runs a code expression, replacing variable names with their respective * values * * @param string $expr expression to evaluate * @param int $ignoreUntil if > 0, it doesn't replace any variables until * that string position, but still evaluates the * whole expr * * @return result of evaluated expression */ function ruleExprEvaluate($expr, $ignoreUntil = 0) { if ($ignoreUntil > 0) { $exprIgnore = substr($expr, 0, $ignoreUntil); $expr = substr($expr, $ignoreUntil); } // Evaluate fired() conditions $expr = preg_replace_callback( '/fired\s*\(\s*(\'|")(.*)\1\s*\)/Ui', array($this, '_ruleExprEvaluateFired'), $expr ); // Evaluate variables $expr = preg_replace_callback( '/\b(\w+)\b/', array($this, '_ruleExprEvaluateVariable'), $expr ); if ($ignoreUntil > 0) { $expr = $exprIgnore . $expr; } $value = 0; $err = 0; // Actually evaluate the code ob_start(); eval('$value = '.$expr.';'); $err = ob_get_contents(); ob_end_clean(); // Error handling if ($err) { throw new Exception( strip_tags($err) . '
Executed code: $value = ' . $expr . ';' ); } return $value; } /** * Reads the rule file into an array, throwing errors messages on syntax * errors. */ static function parseRulesFile() { $file = file('libraries/advisory_rules.txt'); $errors = array(); $rules = array(); $lines = array(); $ruleSyntax = array( 'name', 'formula', 'test', 'issue', 'recommendation', 'justification' ); $numRules = count($ruleSyntax); $numLines = count($file); $ruleNo = -1; $ruleLine = -1; for ($i = 0; $i < $numLines; $i++) { $line = $file[$i]; if ($line[0] == '#' || $line[0] == "\n") { continue; } // Reading new rule if (substr($line, 0, 4) == 'rule') { if ($ruleLine > 0) { $errors[] = sprintf( __('Invalid rule declaration on line %1$s, expected line %2$s of previous rule'), $i + 1, $ruleSyntax[$ruleLine++] ); continue; } if (preg_match("/rule\s'(.*)'( \[(.*)\])?$/", $line, $match)) { $ruleLine = 1; $ruleNo++; $rules[$ruleNo] = array('name' => $match[1]); $lines[$ruleNo] = array('name' => $i + 1); if (isset($match[3])) { $rules[$ruleNo]['precondition'] = $match[3]; $lines[$ruleNo]['precondition'] = $i + 1; } } else { $errors[] = sprintf( __('Invalid rule declaration on line %s'), $i + 1 ); } continue; } else { if ($ruleLine == -1) { $errors[] = sprintf( __('Unexpected characters on line %s'), $i + 1 ); } } // Reading rule lines if ($ruleLine > 0) { if (!isset($line[0])) { continue; // Empty lines are ok } // Non tabbed lines are not if ($line[0] != "\t") { $errors[] = sprintf( __('Unexpected character on line %1$s. Expected tab, but found "%2$s"'), $i + 1, $line[0] ); continue; } $rules[$ruleNo][$ruleSyntax[$ruleLine]] = chop(substr($line, 1)); $lines[$ruleNo][$ruleSyntax[$ruleLine]] = $i + 1; $ruleLine += 1; } // Rule complete if ($ruleLine == $numRules) { $ruleLine = -1; } } return array('rules' => $rules, 'lines' => $lines, 'errors' => $errors); } } function PMA_bytime($num, $precision) { $per = ''; if ($num >= 1) { // per second $per = __('per second'); } elseif ($num*60 >= 1) { // per minute $num = $num*60; $per = __('per minute'); } elseif ($num*60*60 >=1 ) { // per hour $num = $num*60*60; $per = __('per hour'); } else { $num = $num*60*60*24; $per = __('per day'); } $num = round($num, $precision); if ($num == 0) { $num = '<' . pow(10, -$precision); } return "$num $per"; } ?>