<?php

include_once('_dirinfo.php');
include_once(PATH_TO_ROOT. "_support/phpParser/bootstrap.php");

/*
 * This class will validate code provided by user before you are use it in eval function
 * 
 * */
class CodeValiditor
{
	private $errors = array();
	
	/*
	 * 1 Valid code;
	 * 2 Parser error
	 * 3 Unknown function in code
	 * 
	 */
	private $status = 0;
	
	const VALID_CODE = 1;
	
	const PARSER_ERROR = 2;
	
	const UNKNOWN_FUNCTION = 3;
	
	//We get the root of abstract syntax tree statment and then we traverse whole tree and execute all functions
	private function extractFunctions($node, &$functions)
	{
		if($node->left)
		{
			if(get_class($node->left) == "PhpParser\Node\Expr\FuncCall")
				$functions[] = $node->left->name->parts[0];
			
			$this->extractFunctions($node->left, $functions);
		}
		
		if($node->right)
		{
			if(get_class($node->right) == "PhpParser\Node\Expr\FuncCall")
				$functions[] = $node->right->name->parts[0];
			
			$this->extractFunctions($node->right, $functions);
		}
		
		if($node->expr)
		{
			if(get_class($node->expr) == "PhpParser\Node\Expr\FuncCall")
				$functions[] = $node->expr->name->parts[0];
			
			$this->extractFunctions($node->expr, $functions);
		}
		
		if($node->cond)
		{
			if(get_class($node->cond) == "PhpParser\Node\Expr\FuncCall")
				$functions[] = $node->cond->name->parts[0];
			$this->extractFunctions($node->cond, $functions);
		}
	}
	
	//We try to parse provided code. If syntax is ok, we extract all functions and test if they are defined
	private function parseCode($code)
	{
		$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
		try
		{
			$stmts = $parser->parse($code);
		}
		catch(PhpParser\Error $e)
		{
			$this->status = self::PARSER_ERROR;
			$this->errors[] = $e->getMessage();
			return false;
		}
		$functions = array();
		foreach($stmts as $stm)
			$this->extractFunctions($stm, $functions);
		
		return $functions;
		
	}
	 
	public function validateCode($code)
	{
		$this->status = self::VALID_CODE;
		
		/*if(strpos($code, "<?php") === false)
			$code = "<?php " . $code;*/
		
		$functions =  $this->parseCode($code);
	
		foreach($functions as $function)
		{
			if(!function_exists($function))
			{
				$this->status = self::UNKNOWN_FUNCTION;
				$this->errors[] = "Function " . $function .  " doesn't exist!";
			}
		}
		
	}
	
	public function getEvalStaus()
	{
		return $this->status;
	}
	
	public function getErrors()
	{
		return $this->errors;
	}
	
	public function isCodeValid()
	{
		return $this->status == self::VALID_CODE;
	}
}