DslInterpreter.class.php (2811B)
1 <?php 2 3 namespace dsl\executor; 4 5 use dsl\DslAstParser; 6 use dsl\DslException; 7 use dsl\DslLexer; 8 use dsl\standard\Number; 9 use dsl\standard\Script; 10 use dsl\standard\StandardArray; 11 use dsl\standard\StandardDate; 12 use dsl\standard\StandardMath; 13 use dsl\standard\StandardString; 14 use dsl\standard\System; 15 use dsl\standard\Write; 16 17 class DslInterpreter 18 { 19 /** 20 * Execution context. 21 * 22 * @var array 23 */ 24 private $context = []; 25 26 /** 27 * Holds a reference to the write()-Function for getting the output buffer after execution. 28 * @var Write 29 */ 30 private $writer; 31 private $flags; 32 33 const FLAG_SHOW_ERROR = 1; 34 const FLAG_SHOW_TRACE = 2; 35 const FLAG_THROW_ERROR = 4; 36 const FLAG_DEBUG = 8; 37 const FLAG_SECURE = 16; 38 39 private static $secure = true; 40 41 public function __construct( $flags = self::FLAG_SHOW_ERROR + self::FLAG_SECURE ) 42 { 43 $this->flags = $flags; 44 45 self::$secure = boolval($this->flags & self::FLAG_SECURE ); 46 47 // Standard-Globals 48 $this->addContext( [ 49 50 // Standard JS objects 51 'Math' => new StandardMath(), 52 'Array' => new StandardArray(), 53 'String' => new StandardString(), 54 'Number' => new Number(), 55 'Date' => new StandardDate(), 56 57 // Custom Scriptbox objects 58 'System' => new System(), 59 'write' => $this->writer = new Write(), 60 ] ); 61 } 62 63 /** 64 * adds an external context to the interpreter environment. 65 * 66 * @param $context [] 67 */ 68 public function addContext($context ) { 69 $this->context = array_merge( $this->context, $context ); 70 } 71 72 73 /** 74 * Parses and runs the DSL code. 75 * 76 * @param $code String Script-Code 77 * @throws DslException 78 * @return mixed value of last return statement (if any) 79 */ 80 public function runCode( $code ) { 81 82 // Step 1: Splitting the source code into tokens (the "Lexer") 83 $lexer = new DslLexer(); 84 $token = $lexer->tokenize( $code ); 85 86 // Step 2: Creating a syntax tree (abstract syntax tree, AST). 87 try { 88 89 $parser = new DslAstParser(); 90 $parser->parse( $token ); 91 92 //if ( $this->flags & self::FLAG_DEBUG ) 93 // it has no security impact, so lets do it always. 94 $this->addContext( 95 [ 'Script' => new Script( $token,$parser->rootStatement ) ] 96 ); 97 98 // Step 3: Executing the syntax tree. 99 return $parser->execute( $this->context ); 100 } catch ( \Exception $e ) { 101 if ( $this->flags & self::FLAG_SHOW_ERROR ) { 102 if ( $this->flags & self::FLAG_SHOW_TRACE ) 103 $this->writer->buffer .= $e->__toString(); 104 else 105 $this->writer->buffer .= $e->getMessage(); 106 } 107 if ( $this->flags & self::FLAG_THROW_ERROR ) 108 throw $e; 109 } 110 } 111 112 113 /** 114 * Gets the output which was written by the code. 115 * 116 * @return mixed 117 */ 118 public function getOutput() { 119 120 return $this->writer->buffer; 121 } 122 123 /** 124 * @return bool 125 */ 126 public static function isSecure() { 127 return self::$secure; 128 } 129 }