Last active
December 19, 2015 09:29
-
-
Save crisu83/5933618 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Crisu83\PhpExpression; | |
/** | |
* Class that represents a php expression. | |
*/ | |
class Expression | |
{ | |
/** | |
* @var string php code. | |
*/ | |
protected $_code; | |
/** | |
* @var array list of names for function that are allowed to be called (defaults to none). | |
*/ | |
protected $_allowedFunctions = array(); | |
/** | |
* @var array list of language constructs that are allowed to be called (defaults to none). | |
*/ | |
protected $_allowedLanguageConstructs = array(); | |
/** | |
* @var array list of all language constructs in php. | |
*/ | |
protected $_languageConstructs = array( | |
'echo', | |
'empty', | |
'isset', | |
'unset', | |
'exit', | |
'die', | |
'include', | |
'include_once', | |
'require', | |
'require_once', | |
); | |
/** | |
* Creates a new expression. | |
* @param string $code php code. | |
*/ | |
public function __construct(string $code) | |
{ | |
$this->_code = $code; | |
} | |
/** | |
* Checks if the code returns a boolean when evaluated. | |
* @return boolean the result. | |
*/ | |
public function returnsBoolean() | |
{ | |
return is_bool($this->evaluate()); | |
} | |
/** | |
* Checks if the code returns a floating point number when evaluated. | |
* @return boolean the result. | |
*/ | |
public function returnsFloat() | |
{ | |
return is_float($this->evaluate()); | |
} | |
/** | |
* Checks if the code returns an integer when evaluated. | |
* @return boolean the result. | |
*/ | |
public function returnsInteger() | |
{ | |
return is_int($this->evaluate()); | |
} | |
/** | |
* Checks if the code returns a string when evaluated. | |
* @return boolean the result. | |
*/ | |
public function returnsString() | |
{ | |
return is_string($this->evaluate()); | |
} | |
/** | |
* Evaluates the expression using eval(). | |
* @return mixed the result. | |
*/ | |
public function evaluate() | |
{ | |
if (!$this->isSafe()) | |
throw new Exception('Failed to evaluate code. Code is not safe to evaluate.'); | |
return eval($this->code); | |
} | |
/** | |
* @return boolean whether the code is safe to be evaluated. | |
*/ | |
public function isSafe() | |
{ | |
// Make sure that no potentially harmful language constructs are called in the expression. | |
foreach ($this->notAllowedLanguageConstructs() as $languageConstruct) | |
if (preg_match("/$languageConstruct\s*\(?/", $this->_code) !== false) | |
return false; | |
// Make sure that no potentially harmful functions are called in the expression. | |
foreach ($this->notAllowedFunctions() as $functionName) | |
if (preg_match("/$functionName\s*\(/", $this->_code) !== false) | |
return false; | |
return true; | |
} | |
/** | |
* @return array list of prohibited language constructs. | |
*/ | |
protected function notAllowedLanguageConstructs() | |
{ | |
return array_filter($this->_languageConstructs, function($languageConstruct)) { | |
return in_array($this->_allowedLanguageConstructs, $languageConstruct); | |
}); | |
} | |
/** | |
* @return array list of prohibited function names. | |
*/ | |
protected function notAllowedFunctions() | |
{ | |
$definedFunctions = get_defined_functions(); | |
$allFunctions = array_merge($definedFunctions['internal'], $definedFunctions['user']); | |
return array_filter($allFunctions, function($functionName) { | |
return in_array($this->_allowedFunctions, $functionName); | |
}); | |
} | |
/** | |
* @param array $array list of language construct names. | |
*/ | |
public function setAllowedLanguageConstructs(array $array) | |
{ | |
$this->_allowedLanguageConstructs = $array; | |
} | |
/** | |
* @param array $array list of function names. | |
*/ | |
public function setAllowedFunctions(array $array) | |
{ | |
$this->_allowedFunctions = $array; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment