From 585c5395a00088de2d9de17b8583be2f21b52509 Mon Sep 17 00:00:00 2001 From: Joost Faassen Date: Thu, 23 Jul 2020 17:23:42 +0200 Subject: [PATCH] feat: initial checkin --- .editorconfig | 13 +++++ .gitignore | 32 ++++++++++++ LICENSE.md | 19 +++++++ README.md | 57 +++++++++++++++++++++ composer.json | 23 +++++++++ example/example-advanced.lua | 13 +++++ example/example-simple.lua | 9 ++++ example/example.php | 42 +++++++++++++++ lua/sandbox.lua | 43 ++++++++++++++++ src/LuaException.php | 10 ++++ src/LuaSandbox.php | 99 ++++++++++++++++++++++++++++++++++++ 11 files changed, 360 insertions(+) create mode 100755 .editorconfig create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 example/example-advanced.lua create mode 100644 example/example-simple.lua create mode 100644 example/example.php create mode 100644 lua/sandbox.lua create mode 100644 src/LuaException.php create mode 100644 src/LuaSandbox.php diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..383dbae --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.php] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4278655 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +tmp/ +.env +*.tmp +*.bak +*.swp +*~.nib +data/ +local.properties +.settings/ +composer.lock + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml + + +### vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + +### Composer ### +vendor/ +autotune.json diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..80407e7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +# The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d0924fe --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +Lua PHP +======= + +This library enables you to add Lua scripting support to your PHP applications. + +## LuaSandbox + +The LuaSandbox class allows you to easily run user-supplied Lua scripts in an empty sandbox environment. +This means that dangerous functions (i.e. for file and network IO) are unavailable by default. +To make the sandbox useful, you register your own PHP-implemented functions that you allow the chunks to execute. + +## Use-cases + +* Support user-supplied scripts to respond to events in your application +* Advanced expressions, filters, segments +* Customizable routing +* ... and many more :) + +## Usage + +Check the `example/` directory for a well-documented example. + +## About Lua + +* Website: http://www.lua.org/ +* Wikipedia: https://en.wikipedia.org/wiki/Lua_(programming_language) + +## Requirements + +This library requires that the [PHP Lua extension](https://www.php.net/manual/en/book.lua.php) is installed. + +A quick install guide for Ubuntu: + +```sh +# Install lua library +apt-get install -y --no-install-recommends lua5.3 liblua5.3-dev +# pecl expects liblua and includes in specific locations, so move them around a bit: +cp /usr/lib/x86_64-linux-gnu/liblua5.3.a /usr/lib/liblua.a +cp /usr/lib/x86_64-linux-gnu/liblua5.3.so /usr/lib/liblua.so +ln -s /usr/include/lua5.3 /usr/include/lua +# Install the lua extension through pecl +pecl install lua +# Activate the lua extension in your PHP config +php --ini # find out where your PHP config files are located +echo "extension=lua.so" > /path/to/my/php/conf.d/lua.ini +``` + +## License + +MIT. Please refer to the [license file](LICENSE) for details. + +## Brought to you by the LinkORB Engineering team + +
+Check out our other projects at [linkorb.com/engineering](http://www.linkorb.com/engineering). + +Btw, we're hiring! diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4914d7c --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "linkorb/lua", + "description": "Lua PHP: Library for extending your application with lua scripts", + "homepage": "http://www.github.com/linkorb/lua-php", + "keywords": ["php", "lua", "scripting", "sandbox", "linkorb"], + "type": "library", + "authors": [ + { + "name": "LinkORB Engineering", + "email": "engineering@linkorb.com", + "role": "Development" + } + ], + "require": { + "php": ">=7.2.0" + }, + "autoload": { + "psr-4": { + "LinkORB\\Lua\\": "src/" + } + }, + "license": "MIT" +} diff --git a/example/example-advanced.lua b/example/example-advanced.lua new file mode 100644 index 0000000..72ea513 --- /dev/null +++ b/example/example-advanced.lua @@ -0,0 +1,13 @@ +local function on_click(data) + -- error("BWAM") + print("On click!!") +end + +return { + name = "My advanced chunk", + description = "This is an example structure", + on_click = on_click, + on_exit = function(data) + print("I'll be back") + end, +} diff --git a/example/example-simple.lua b/example/example-simple.lua new file mode 100644 index 0000000..3f23505 --- /dev/null +++ b/example/example-simple.lua @@ -0,0 +1,9 @@ +-- This is an example lua file to load into the sandbox + +return function(foo, bar) + print(reverse(foo) .. ", " .. bar) + -- local f = io.open('/etc/hostname', "rb") + -- local content = f:read "*a" + -- print(content) + return "This is returned from Lua" +end diff --git a/example/example.php b/example/example.php new file mode 100644 index 0000000..feaadfd --- /dev/null +++ b/example/example.php @@ -0,0 +1,42 @@ +registerCallback('print', function($text) { + echo "PRINT: " . $text . PHP_EOL; +}); + +$sandbox->registerCallback('reverse', function($text) { + return strrev($text); +}); + +// Let's register some callable Lua chunks: +$code = file_get_contents(__DIR__ . '/example-simple.lua'); +$sandbox->registerChunk('example-simple', $code); + +$code = file_get_contents(__DIR__ . '/example-advanced.lua'); +$sandbox->registerChunk('example-advanced', $code); + + +// You can now call a chunk (assuming it is a function) with arguments: +$res = $sandbox->callChunk('example-simple', ['hello', 'world']); +echo "RETURN VALUE: " . $res . PHP_EOL; + +// You can also retrieve a chunk by name: +$chunk = $sandbox->getChunk('example-advanced'); +print_r($chunk); +$chunk['on_click']('Click test'); + +// You can retrieve the environment for inspection: +$e = $sandbox->getEnvironment(); +print_r($e); + +echo "Done\n"; diff --git a/lua/sandbox.lua b/lua/sandbox.lua new file mode 100644 index 0000000..0cd8f77 --- /dev/null +++ b/lua/sandbox.lua @@ -0,0 +1,43 @@ +local sandbox_env = { + -- start with an empty environment +} + +-- array of registered chunks +local chunks = {} + +function registerChunk(chunk_name, code) + local chunk, message = load(code, 'sandbox:' .. chunk_name, 't', sandbox_env) + if not chunk then + error("Failed to register chunk: " .. chunk_name) + end + chunks[chunk_name] = chunk() +end + +-- promote an item in the global _ENV to the sandbox env +function registerCallback(name) + sandbox_env[name] = _ENV[name] +end + +-- call a chunk by name as a function with parameters +function callChunk(chunk_name, ...) + local chunk = getChunk(chunk_name) + local status, res = pcall(chunk, ...) + if (not status) then + error(res) + end + return res +end + +-- retrieve a chunk by name +function getChunk(chunk_name) + local chunk = chunks[chunk_name] + if (not chunk) then + error("Getting unregistered chunk: " .. chunk_name) + end + return chunk +end + +-- retrieve the sandbox environment for inspection +function getEnvironment() + return sandbox_env +end diff --git a/src/LuaException.php b/src/LuaException.php new file mode 100644 index 0000000..2d02203 --- /dev/null +++ b/src/LuaException.php @@ -0,0 +1,10 @@ +lua = $lua; + + $this->lua->registerCallback("error", function($message) { + $this->error($message); + }); + + $this->lua->registerCallback("cool", function($message) { + echo "COOL: $message\n"; + }); + + $this->lua->registerCallback("print", function($message, $a = null, $b = null, $c = null) { + echo "PRINT: $message $a $b $c\n"; + }); + + $sandboxCode = file_get_contents(__DIR__ . '/../lua/sandbox.lua'); + $this->lua->eval($sandboxCode); + } + + public function registerChunk(string $name, string $code): void + { + $res = $this->lua->call("registerChunk", array($name, $code)); + if ($res) { + throw new LuaException("Failed to register chunk " . $name); + } + } + + public function registerCallback(string $name, callable $cb) + { + $this->lua->registerCallback($name, $cb); + $res = $this->lua->call("registerCallback", array($name)); + } + + + public function getChunk(string $name) + { + $res = $this->lua->call("getChunk", array($name)); + return $res; + } + + + public function callChunk(string $name, $arguments) + { + array_unshift($arguments, $name); + $res = $this->lua->call("callChunk", $arguments); + return $res; + } + + public function getEnvironment() + { + $res = $this->lua->call("getEnvironment", []); + return $res; + } + + + +// $x = $lua->call("getChunk", array('demo-advanced')); +// if (!$x) { +// echo "PHP: That failed\n"; +// } +// print_r($x); +// $f = $x['onFrame']; +// try { +// $f('ahaha'); +// } catch (\Exception $e) { +// print_r($e); +// } +// $f = ($x['onTerminate'] ?? null); +// $f("bye", "x"); + +// echo "Done\n"; + +// } + +}