Skip to content

Instantly share code, notes, and snippets.

@crisu83
Last active December 19, 2015 09:29
Show Gist options
  • Save crisu83/5933618 to your computer and use it in GitHub Desktop.
Save crisu83/5933618 to your computer and use it in GitHub Desktop.
<?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