<?php
 namespace App\Spotlight\Source; class MathOperations extends \App\Spotlight\SourceAbstract { protected $cacheKey = null; public function __construct() { parent::__construct(); $this->cacheKey = sprintf("%s:%s", __CLASS__, CURRENT_ID_USER); } protected function determineRecords($offset, $resultsPerPage):array { $return = []; $this->searchString = rtrim($this->searchString, '+-*/'); if ($this->extractor->contains("/^[0-9\.\s\(]+[\+\-\/\*\(\)]+[0-9\.\+\-\/\*\(\)\s]+$/i", $this->searchString)) { if ($this->isValidMathExpression($this->searchString)) { $fcnCode = sprintf("\$ret = function() {return %s;}; return \$ret();", $this->searchString); $prettyInput = $this->searchString; $prettyInput = preg_replace("/([\+\-\*\/]+)/i", ' $1 ', $prettyInput); $prettyInput = preg_replace("/[\s]+([\*\/]+)[\s]+/i", '$1', $prettyInput); $prettyInput = trim($prettyInput); $shouldTruncate = (strlen($prettyInput)>23); error_clear_last(); $evalResult = @eval($fcnCode); if (error_get_last()) { $result = 0; $error = error_get_last(); $record = new \App\Spotlight\Record( 'An error occured while evaluating the math expression', ($shouldTruncate? '...'.substr($prettyInput, -20):$prettyInput), sprintf("spotlight_autocomplete.setValue({value:'%s' });", $this->searchString), 0, ['color-important', ] ); $record->tooltip = $error['message']; $return[]= $record; } else { $result = round($evalResult, 2); $record = new \App\Spotlight\Record( $result, ($shouldTruncate? '...'.substr($prettyInput, -20):$prettyInput), sprintf("if(null===window.prompt('%s =', '%s')){ spotlight_autocomplete.setValue({value:'%s' }); }", $prettyInput, $result, $this->searchString), 0, ['color-important', ] ); if ($shouldTruncate) { $record->tooltip = $prettyInput; } $return[]= $record; $history = \Engine\Cache\General::getInstance('disk')->get($this->cacheKey); if (!$history) { $history = []; } foreach (array_reverse($history) as $hist) { $shouldTruncate = (strlen($hist['input'])>23); if ($hist['input']!=$prettyInput && $this->matchesSearchString($hist['input'])) { $record = new \App\Spotlight\Record( $hist['result'], ($shouldTruncate? '...'.substr($hist['input'], -20):$hist['input']), sprintf("if(null===window.prompt('%s =', '%s')){ spotlight_autocomplete.setValue({value:'%s' }); }", $hist['input'], $hist['result'], $hist['input']), 10, ['text-italic', ] ); if ($shouldTruncate) { $record->tooltip = $hist['input']; } $return[]= $record; } } $history[]= [ 'input' => $prettyInput, 'result' => $result, ]; $keys = []; foreach ($history as $idx=>$hist) { $k = $hist['input']; if (in_array($k, $keys)) { unset($history[$idx]); } $keys[]= $k; } $history = array_values(array_slice($history, -50)); \Engine\Cache\General::getInstance('disk')->set($this->cacheKey, $history, 60*60*24*7); } } } return $return; } protected function matchesSearchString($label) { $searchStr = preg_replace("/[^0-9]+/i", ' ', $this->searchString); $labelStr = preg_replace("/[^0-9]+/i", ' ', $label); return $this->extractor->startsWith($searchStr, $labelStr) || $this->extractor->startsWith($labelStr, $searchStr); } protected function isValidMathExpression($str) { $number = "\d+(\.\d+)?"; $ident = "[a-z]\w*"; $atom = "[+-]?($number|$ident)"; $op = "[+*/-]"; if (!$this->extractor->contains("~{$op}{2,}~i", $str)) { $sexpr = "$atom($op$atom)*"; $str = preg_replace('~\s+~', '', $str); $par = "~\($sexpr\)~"; while (preg_match($par, $str)) { $str = preg_replace($par, 'x', $str); } return preg_match("~^$sexpr$~", $str); } return false; } } 