commit 12b41cbdf124960e6204ee6a656c7901cfe838a3
parent 326a85625915ed8fe4c29330184d2f81305ccd88
Author: Jan Dankert <develop@jandankert.de>
Date: Mon, 27 Jun 2022 00:41:50 +0200
Fetched from upstream.
Diffstat:
17 files changed, 261 insertions(+), 51 deletions(-)
diff --git a/dsl/ast/DslFunctionCall.class.php b/dsl/ast/DslFunctionCall.class.php
@@ -43,12 +43,7 @@ class DslFunctionCall implements DslStatement
if ( ! is_array($parameterValues)) $parameterValues = array($parameterValues);
- if ( $function instanceof \dsl\context\DslFunction ) {
- // call "external" native function
-
- return call_user_func_array(array($function,'execute'),$parameterValues );
- }
- elseif ( $function instanceof DslFunction ) {
+ if ( $function instanceof DslFunction ) {
$parameters = $function->parameters;
@@ -56,7 +51,7 @@ class DslFunctionCall implements DslStatement
throw new DslRuntimeException('function call has '.sizeof($parameterValues).' parameters but the function has '.sizeof($parameters).' parameters');
// Put all function parameters to the function context.
- $parameterContext = array_combine( $parameters, $parameterValues );
+ $parameterContext = array_combine( $parameters, array_slice($parameterValues,0,sizeof($parameters)) );
$subContext = array_merge( $context,$parameterContext );
return $function->execute( $subContext );
diff --git a/dsl/ast/DslProperty.class.php b/dsl/ast/DslProperty.class.php
@@ -3,6 +3,7 @@
namespace dsl\ast;
use cms\generator\dsl\DslObject;
+use dsl\context\Scriptable;
use dsl\DslRuntimeException;
class DslProperty implements DslStatement
@@ -40,6 +41,11 @@ class DslProperty implements DslStatement
// copy object methods to the object context to make them callable.
foreach( get_class_methods( $object ) as $method ) {
$objectContext[ $method ] = function() use ($method, $object) {
+
+ // For Security: Do not expose all available objects, they must implement a marker interface.
+ if ( ! $object instanceof Scriptable )
+ throw new DslRuntimeException('security: Object '.get_class($object).' is not scriptable and therefore not available in script context');
+
return call_user_func_array( array($object,$method),func_get_args() );
};
}
@@ -55,10 +61,6 @@ class DslProperty implements DslStatement
$prop = $this->property->execute( $objectContext );
- // TODO: how to recognize objects
- // For Security: Do not expose internal objects.
- //if ( is_object($prop) && ! $prop instanceof DslObject )
- // $prop = '@'.get_class($prop).'@';
return $prop;
}
diff --git a/dsl/ast/DslVariable.class.php b/dsl/ast/DslVariable.class.php
@@ -20,7 +20,7 @@ class DslVariable implements DslStatement
public function execute( & $context ) {
if ( ! array_key_exists( $this->name, $context ) )
- throw new DslRuntimeException('variable \''.$this->name.'\' does not exist.');
+ throw new DslRuntimeException('\''.$this->name.'\' does not exist');
return $context[ $this->name ];
}
diff --git a/dsl/context/BaseScriptableObject.class.php b/dsl/context/BaseScriptableObject.class.php
@@ -0,0 +1,20 @@
+<?php
+
+
+namespace dsl\context;
+
+use dsl\standard\Helper;
+
+class BaseScriptableObject implements Scriptable
+{
+
+ public function __toString()
+ {
+ return "Script object";
+ }
+
+ public function help()
+ {
+ return Helper::getHelp($this);
+ }
+}
+\ No newline at end of file
diff --git a/dsl/context/DslFunction.class.php b/dsl/context/DslFunction.class.php
@@ -1,7 +0,0 @@
-<?php
-
-namespace dsl\context;
-
-interface DslFunction
-{
-}
-\ No newline at end of file
diff --git a/dsl/context/DslObject.class.php b/dsl/context/DslObject.class.php
@@ -1,8 +0,0 @@
-<?php
-
-namespace dsl\context;
-
-interface DslObject
-{
-
-}
-\ No newline at end of file
diff --git a/dsl/context/Scriptable.class.php b/dsl/context/Scriptable.class.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace dsl\context;
+
+/**
+ * Class is callable from scripts.
+ *
+ * Classes whose methods should be callable from user scripts must implement this interface.
+ *
+ * This is a marker interface which do not has methods by design.
+ *
+ * If a class do not implement this interface then its methods cannot be called out of a script.
+ * This is for security reasons: You cannot expose your classes to the user context by mistake.
+ */
+interface Scriptable {}
+\ No newline at end of file
diff --git a/dsl/executor/DslInterpreter.class.php b/dsl/executor/DslInterpreter.class.php
@@ -9,6 +9,7 @@ use dsl\standard\Script;
use dsl\standard\StandardArray;
use dsl\standard\StandardDate;
use dsl\standard\StandardMath;
+use dsl\standard\System;
use dsl\standard\Write;
class DslInterpreter
@@ -38,6 +39,7 @@ class DslInterpreter
// Standard-Globals
$this->addContext( [
+ 'System'=> new System(),
'Math' => new StandardMath(),
'Array' => new StandardArray(),
'Date' => new StandardDate(),
diff --git a/dsl/standard/Date.class.php b/dsl/standard/Date.class.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace dsl\standard;
+
+use dsl\context\BaseScriptableObject;
+
+
+/**
+ * Date.
+ *
+ * Similar to the javascript Date object
+ */
+class Date extends BaseScriptableObject
+{
+ private $time;
+
+ /**
+ * @param integer $time unix timestamp
+ */
+ public function __construct( $time = null )
+ {
+ if ( $time == null )
+ $time = time();
+
+ $this->time = $time;
+ }
+
+
+ public function getDate() { return date('d',$this->time); }
+ public function getDay() { return date('w',$this->time); }
+ public function getFullYear() { return date('Y',$this->time); }
+ public function getHours() { return date('H',$this->time); }
+ public function getMilliseconds() { return 0; }
+ public function getMinutes() { return date('i',$this->time); }
+ public function getMonth() { return date('m',$this->time); }
+ public function getSeconds() { return date('s',$this->time); }
+ public function getTime() { return $this->time * 1000; }
+ public function getTimezoneOffset() { return date('y',$this->time)/60;}
+ public function getUTCDate() { return date('d',$this->time);}
+ public function getUTCDay() { return date('w',$this->time);}
+ public function getUTCFullYear() { return date('Y',$this->time);}
+ public function getUTCHours() { return date('H',$this->time);}
+ public function getUTCMilliseconds() { return 0;}
+ public function getUTCMinutes() { return date('i',$this->time);}
+ public function getUTCMonth() { return date('m',$this->time);}
+ public function getUTCSeconds() { return date('s',$this->time);}
+ public function getYear() { return date('y',$this->time); }
+
+ public function __toString()
+ {
+ return date('r');
+ }
+
+
+ /**
+ * @return string
+ */
+ public function help()
+ {
+ return Helper::getHelp($this);
+ }
+}
+\ No newline at end of file
diff --git a/dsl/standard/Helper.class.php b/dsl/standard/Helper.class.php
@@ -0,0 +1,14 @@
+<?php
+
+
+namespace dsl\standard;
+
+
+class Helper
+{
+ public static function getHelp( $obj ) {
+
+ return 'Object properties: '.implode(', ',array_map(function($property) { return ''.$property; },get_object_vars($obj))).';'.
+ ' methods: '.implode(', ',array_map(function($property) { return ''.$property.'()'; },get_class_methods($obj)));
+ }
+}
+\ No newline at end of file
diff --git a/dsl/standard/Script.class.php b/dsl/standard/Script.class.php
@@ -3,10 +3,10 @@
namespace dsl\standard;
use dsl\ast\DslStatement;
-use dsl\context\DslObject;
+use dsl\context\BaseScriptableObject;
use dsl\DslToken;
-class Script implements DslObject
+class Script extends BaseScriptableObject
{
/**
* @var DslToken[]
@@ -53,4 +53,17 @@ class Script implements DslObject
{
return print_r($this->ast,true);
}
+
+ public function __toString()
+ {
+ return "Script Info, call help() for help.";
+ }
+
+ /**
+ * @return string
+ */
+ public function help()
+ {
+ return Helper::getHelp($this);
+ }
}
\ No newline at end of file
diff --git a/dsl/standard/StandardArray.class.php b/dsl/standard/StandardArray.class.php
@@ -1,11 +1,26 @@
<?php
namespace dsl\standard;
-class StandardArray
+use dsl\context\BaseScriptableObject;
+
+class StandardArray extends BaseScriptableObject
{
public function of() {
return func_get_args();
}
+
+ public function __toString()
+ {
+ return "Arrays:Object";
+ }
+
+ /**
+ * @return string
+ */
+ public function help()
+ {
+ return Helper::getHelp($this);
+ }
}
\ No newline at end of file
diff --git a/dsl/standard/StandardDate.class.php b/dsl/standard/StandardDate.class.php
@@ -1,7 +1,9 @@
<?php
namespace dsl\standard;
-class StandardDate
+use dsl\context\BaseScriptableObject;
+
+class StandardDate extends BaseScriptableObject
{
/**
* Date.now()
@@ -14,4 +16,45 @@ class StandardDate
return time();
}
+
+
+
+ /**
+ * Gets the current date object.
+ * @return Date
+ */
+ public function getDate( $date = null ) {
+
+ return new Date( $date );
+ }
+
+
+
+ /**
+ * Gets the current date object for a given date.
+ * @return Date
+ */
+ public function getDateFor( $year = 0,$month = 0,$day = 0,$hour = 0,$minute = 0,$second = 0 ) {
+
+ $month++; // month in JS is 0-based, but in PHP not.
+
+ return new Date( mktime( $hour, $minute, $second, $month, $day, $year ) );
+ }
+
+
+ public function __toString()
+ {
+ return "Arrays:Object";
+ }
+
+ public function help()
+ {
+ return Helper::getHelp($this);
+ }
+
+
+ public function parse( $dateAsString ) {
+
+ return strtotime( $dateAsString );
+ }
}
\ No newline at end of file
diff --git a/dsl/standard/StandardMath.class.php b/dsl/standard/StandardMath.class.php
@@ -1,7 +1,9 @@
<?php
namespace dsl\standard;
-class StandardMath
+use dsl\context\BaseScriptableObject;
+
+class StandardMath extends BaseScriptableObject
{
public $E = M_EULER;
public $PI = M_PI;
diff --git a/dsl/standard/System.class.php b/dsl/standard/System.class.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace dsl\standard;
+
+use dsl\context\BaseScriptableObject;
+
+class System extends BaseScriptableObject
+{
+ /**
+ * runtime
+ * @var string
+ */
+ public $version;
+
+ /**
+ * Operating system
+ * @var string
+ */
+ public $os;
+
+ public function __construct()
+ {
+ $this->version = PHP_VERSION;
+ $this->os = PHP_OS;
+ }
+
+ /**
+ * @param $name
+ * @return array|false|string
+ */
+ public function env( $name ) {
+
+ return getenv('SCRIPTBOX_'.$name);
+ }
+}
+\ No newline at end of file
diff --git a/dsl/standard/Write.class.php b/dsl/standard/Write.class.php
@@ -2,13 +2,11 @@
namespace dsl\standard;
-use dsl\context\DslFunction;
-
-class Write implements DslFunction
+class Write
{
public $buffer;
- public function execute( $text )
+ public function __invoke( $text )
{
$this->buffer .= $text;
}
diff --git a/index.php b/index.php
@@ -5,7 +5,7 @@ use dsl\executor\DslInterpreter;
require('./autoload.php');
?><html>
-<head><title>Script Sandbox</title>
+<head><title>Scriptbox</title>
<style>
textarea {
width: 100%;
@@ -16,9 +16,8 @@ require('./autoload.php');
<body>
-</body></html>
-<h1>Script Sandbox</h1>
-<p>Script sandbox for PHP. The syntax is a subset of Javascript. The interpreter is supporting functions, full arithmetic calculations, for-loops, if-else statements.
+<h1>Scriptbox</h1>
+<p>Scriptbox is a Script sandbox for PHP.</p><p>The syntax is a subset of Javascript.<br/> The interpreter is supporting functions, full arithmetic calculations, for-loops, if-else statements.
</p>
<?php $code = @$_POST['code'] ?: <<<DEF
// Script sandbox
@@ -64,12 +63,12 @@ for( name of names ) {
DEF
; ?>
-<legend title="Script output">
-<pre>
- <?php
+<fieldset>
+<legend>Output</legend>
+<pre><?php
try {
error_reporting( E_ALL );
- $interpreter = new DslInterpreter( DslInterpreter::FLAG_SHOW_ERROR + DslInterpreter::FLAG_SHOW_TRACE );
+ $interpreter = new DslInterpreter( DslInterpreter::FLAG_SHOW_ERROR );
$interpreter->runCode( $code );
echo htmlentities( $interpreter->getOutput() );
} catch( Exception $e ) {
@@ -77,8 +76,14 @@ DEF
echo "Unexcepted error while running the script: \n".htmlentities( $e->getMessage() )."\n";
}
?>
-</pre></legend>
-<form action="./" method="POST"><textarea name="code"><?php echo htmlentities( $code ) ?>
-</textarea>
-<input type="submit" value="Execute code"/>
-</form>
+</pre>
+</fieldset>
+ <fieldset>
+ <legend>Source</legend>
+ <form action="./" method="POST">
+ <textarea name="code" rows="50" style="border:0;"><?php echo htmlentities( $code ) ?></textarea>
+ <input type="submit" value="Execute" />
+ </form>
+ </fieldset>
+
+</body></html>
+\ No newline at end of file