Giter Club home page Giter Club logo

uniter's Introduction

Uniter PHP

Build Status Join the chat at https://gitter.im/asmblah/uniter

Run PHP client-side in the browser or in Node.js.

Manipulating the DOM using PHP with Uniter

Try it now

Demos

Packages

Uniter is split into several NPM packages, each with a separate repository:

Package Version Dependencies
uniter npm Dependency Status
phptoast npm Dependency Status
phptojs npm Dependency Status
phpcore npm Dependency Status
phpruntime npm Dependency Status
phpcommon npm Dependency Status
phpify npm Dependency Status
dotphp npm Dependency Status

uniter is the main Uniter library (this repository). It pulls in all the required components (below) to take a string of PHP code, evaluate it and return the result with a simple API.

phptoast is the parser for Uniter. It takes PHP code as a string and returns an AST comprised of plain JavaScript objects.

phptojs is the transpiler. It takes an AST (such as the one returned by phptoast) and translates it into JavaScript that can then be executed.

phpcore is the minimal runtime library required for code transpiled by phptojs to execute. It contains some builtin PHP classes and functions, but only those that are required (eg. the Closure class or spl_autoload_register(...) function).

phpruntime is the extended "full" runtime library. After pulling in phpcore, it installs the remaining builtin classes and functions, such as array_merge(...). Only a small subset of PHP's standard library has been implemented so far - please open a GitHub issue in the phpruntime repository if you would like to request something that is missing.

phpcommon contains various tools that are shared between the different packages, such as the PHPFatalError class used by both the parser (phptoast) and runtime (phpcore).

phpify is a Browserify transform that can be used to require PHP modules (and entire libraries) from JavaScript. For an example of compiling a PHP library down to JavaScript, see the Uniter Symfony EventDispatcher demo.

dotphp allows for easily including PHP files from Node.js. A require(...) extension may be installed by using the /register script or PHP files may simply be required with the exported .require(...) method. Stderr and stdout are mapped to the process' stderr and stdout respectively, and the filesystem/ include/require/_once(...) access is mapped to the real filesystem.

Getting started

$ npm install uniter
$ node
> var php = require('uniter').createEngine('PHP');
> php.getStdout().on('data', function (text) { console.log(text); });
> php.execute('<?php print "Hello from PHP!";');
Hello from PHP!

Features

  • Environment-agnostic architecture: should run in any modern browser (IE < 9 support coming soon) and Node.js

  • PHP statements, constructs and operators:

    • if, else and else if statements
    • while loop support
    • for loop support
    • foreach loop support
    • function statements with type hinting (as syntactic sugar only: no enforcement is performed yet)
    • Closure function expressions
    • switch statements
    • Forward and backward goto statements (but no overlap support yet)
    • class object support (new operator, extends support etc.)
    • Instance property/method access (-> operator)
    • Static class property/method access (:: operator), self:: construct
    • use statement for class, namespace and function importing and aliasing
    • Magic __autoload(...) function
    • Magic __DIR__, __FILE__ and __LINE__ constants
    • Ternary operator
    • Loose equality == and inequality != comparison operators
    • Strict equality === and inequality !== comparison operators

    And others... see the Engine integration tests for more info.

Using on the command line

You can use Uniter from the command line after installing it via NPM, eg.:

# Install Uniter globally
$ npm install -g uniter

# Execute PHP code
$ uniter -r 'echo 7 + 2;'
9

# Parse PHP but just dump the AST as JSON, don't attempt to execute
$ uniter -r 'echo 7 + 2;' --dump-ast
{
    "statements": [
        {
            "expression": {
                "left": {
                    "number": "7",
                    "name": "N_INTEGER"
                },
                "right": [
                    {
                        "operator": "+",
                        "operand": {
                            "number": "2",
                            "name": "N_INTEGER"
                        }
                    }
                ],
                "name": "N_EXPRESSION"
            },
            "name": "N_ECHO_STATEMENT"
        }
    ],
    "name": "N_PROGRAM"
}

Keeping up to date

Running the tests

There are two supported ways of running the Mocha test suite:

  1. Run the tests in Node.js from the command line:

     cd uniter/
     npm test
    
  2. Run the tests in a browser by starting a Node.js server:

     npm run-script webtest
    

    You should then be able to run the tests by visiting http://127.0.0.1:6700 in a supported web browser.

License

MIT

Contributors

Pharaoh Tools

uniter's People

Contributors

asmblah avatar gitter-badger avatar jcubic avatar phpengine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uniter's Issues

Unreachable Code after return statement

Hi @asmblah ,

I see this a lot, probably hundreds of times a day actually :D . I've been ignoring it until I had the other bits reasonably stable, and I guess that's about now.

For instance, the console on the following page...
http://www.devcloud.isophp.org.uk/

Has over 600 appearances of unreachable code after return statement

How do I get rid of these? Or do I need to?

Thanks, Dave

Beta

Uniter is described as being in beta state. Is there anything I can do to help bring it into what you'd call beta, alpha, RC, 1.0, etc. ?

HTML Tags for Uniter

Could something like this be possible?

<script type="text/uniter">
	echo "This is a PHP statement" ;
</script>

Cheers

Add support for interface

I've tried

interface Yopyop {
}

I've got :

PHP Parse error: syntax error, unexpected 'Y' in (program) on line 3

Did I miss something ? ๐Ÿ‘“

CLI tooling

Hey.

I have completed my concept for the WebPack loader. It wont be easy to make but I have a plan for it.

However, in order to be able to see the results of various statement compilations, I need to be able to dump the generated source. That is, where I thought, the CLI would come in handy.

Currently, the Uniter command is pretty simple - it grabs a file and runs it.

These features would be nice, if it would have them:

  • Output transpiled JS code.
  • script to run before the actual Uniter starts, allowing the script to install functions and such.
  • Specifying sync or async mode.
--dump-js | Dump the generated JavaScript to STDOUT.
--before <script> | Script to run before Uniter.
--mode=sync/async | Specify mode

What do you think?

WebPack support

I admit it. PHP still is a language I prefer over JS... And that is why I want to use it properly.

A while ago, I had discovered WebPack. It allows me to use Loaders in order to transpile stuff to pure JS. So I wanted to use uniter. Problem is, that even a simple bit of testing fails:

[email protected] ~/W/BIRD3 $ uniter -r 'echo 7 + 2;' --dump-ast
fs.js:792
  return binding.lstat(pathModule._makeLong(path));
                 ^
Error: ENOENT, no such file or directory '/Users/Ingwie/Work/BIRD3/uniter.js'
    at Error (native)
    at Object.fs.lstatSync (fs.js:792:18)
    at Object.realpathSync (fs.js:1383:21)
    at modular.configure.transport (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:57:31)
    at Module.util.extend.load (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:363:56)
    at Module.eval (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:277:40)
    at Object.util.each (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:44:38)
    at loadDependencies (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:276:30)
    at eval (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:340:29)
    at Funnel.util.extend.done (eval at <anonymous> (/usr/local/lib/node_modules/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:122:25)
[email protected] ~/W/BIRD3 $ node --version
v0.12.2

Why isn't uniter working in NodeJS?

Production Settings

Hi Dude,

Long time no hear, How goes it? This might be quite a complex issue, but hopefully not :)

So, We've got Uniter running Isomorphic code pretty smoothly. It runs really fast in Desktop (Electron) but quite slowly sometimes on Cordova/Web. I think the reason is because the autoloader is using PHP Server style includes/requires to load classes.

This seems like a great way to work in development, as everything is always accessible to the developer plainly, but it can be quite slow in Web and especially Cordova.

So, the question is, what's the best way for me to turn that into "Production Settings". I was thinking, do I need to load absolutely everything at the beginning with a Progress Bar? Or a way of compression, minify, obfuscate or something, or would that break the Parser?

An example is here: http://www.isophp.org.uk , if you watch the firebug console/network while the page is loading, I think you'll see what I mean

Thanks, Dave

Common functions

This is partially a note to myself.

<?php
$funcs = array(
    "is_callable", "is_number", "is_null",
    "file_exists",
    "class_exists"
);
foreach($funcs as $func) {
    echo "$func => ".(function_exists($func) ? "Yes" : "No")."\n";
}

All of them are "No".

Uniter STL should have these soon. However, empty() also does not exist. This is more a keyword-function, and should be a built-in.

Logo?

Is there a logo? Can I get someone to make one and you choose/own it etc? I've got some designers doing other bits too, it'll probably only take an hour two, and it might make the sites a bit more aesthetic.

Documentation : Benchmark?

I have been doing some digging but haven't really found a performance comparision of JS transpiled PHP vs PHP running natively.

I'm sure it's slower, I would be highly amused if it were faster (even in corner cases like serving 'hello world' )

Adding items to array via []

<?php
$filters = array();
$filters[] = 1;  // PHP Parse error: syntax error, unexpected ']' in (program) on line 3
$filters[] = 2;
<?php
class A {

    private $filters = array();

    public function addFilters($x) {
        $this->filters[] = $x; // PHP Parse error: syntax error, unexpected ']' in (program) on line 7
    }
}

Fix intermittent errors when running tests

node tests/bdd/index.js --grep 'equality operator'

fs.js:679
  return binding.lstat(pathModule._makeLong(path));
                 ^
Error: ENOENT, no such file or directory '/Library/WebServer/Documents/localhost/uniter/tests/bdd/languages'
    at Object.fs.lstatSync (fs.js:679:18)
    at Object.realpathSync (fs.js:1265:21)
    at modular.configure.transport (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:57:31)
    at Module.util.extend.load (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:363:56)
    at Module.eval (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:277:40)
    at Object.util.each (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:44:38)
    at loadDependencies (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:276:30)
    at eval (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:340:29)
    at Funnel.util.extend.done (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:122:25)
    at resolveDependencies (eval at <anonymous> (/Library/WebServer/Documents/localhost/uniter/node_modules/modular-amd/index.js:23:25), <anonymous>:338:32)

Include transport when run on CLI

Hi, @asmblah , how goes?

I'm having an issue when running uniter on CLI. I ran
sudo npm install -g uniter

But the require command doesn't appear to work...

pharaoh@pharaohbook:/tmp$ uniter -r 'require("/tmp/uniter_executor.php");'
include(/tmp/uniter_executor.php) :: No "include" transport is available for loading the module.

With a node version of the following

pharaoh@pharaohbook:/tmp$ node -v
v8.10.0

Thanks in advance

Uncaught TypeError: propertyData.value is not a function

Hi @asmblah ,

So, this one is a bit more difficult for me to point out the code of, but I've got it down to it happening during the instantiation of a class. I'm not sure what the best way to display a demo of the failed code is, as I'm running this within PHPDesktop/Chrome.

When I Try to instantiate this class...
https://gist.github.com/phpengine/6ea043374e25b48364d9d88573007310

Which is called from line 21 of this...
https://gist.github.com/phpengine/7f9d9382f3df722f6b749cb410fefe94

Then I get the following fatal error...

Uncaught TypeError: propertyData.value is not a function
    at prod_bundle.js:35976
    at Object.module.exports [as forOwn] (prod_bundle.js:4205)
    at ObjectValue.InternalClass (prod_bundle.js:35974)
    at Class.instantiate (prod_bundle.js:36870)
    at BarewordStringValue.instantiate (prod_bundle.js:39993)
    at Tools.createInstance (prod_bundle.js:33339)
    at Scope._main (prod_bundle.js:43099)
    at ObjectValue.wrapperFunc (prod_bundle.js:35449)
    at callMethod (prod_bundle.js:36530)
    at Class.callMethod (prod_bundle.js:36586)

The code in Uniter that appears to be being triggered is this...

                    // Go through and define the properties and their default values
                    // on the object from the class definition by initialising them
                    _.forOwn(definition.properties, function (propertyData, name) {
                        var instanceProperty = properties[name],
                            initialValue = propertyData.value();

Any ideas as to what might be causing this please? Or, is there an alternate way you'd like to to run / host the code that would help?
I've uploaded the whole thing in a zip file that will run on linux, if you download the following
https://repositories.internal.pharaohtools.com/index.php?control=BinaryServer&action=serve&item=uniter_phpdesktop_bug

Then extract the tarball

cd ptv_gui/phpdesktop
./phpdesktop --remote-debugging-port=10101

Then the application will open, and the terminal output will show the error. Also, it'll open the chrome debugging port, and 127.0.0.1:10101 will show the chrome console.

include_once: Not resolved

include_once is resolved as a regular function.

[email protected] /tmp $ nano test.php
[email protected] /tmp $ cat test.php
<?php
include_once "foo.php";
[email protected] /tmp $ uniter2ast test.php 

/Users/Ingwie/Work/uniter/uniter-tools/node_modules/phptoast/src/ErrorHandler.js:43
        throw error;
        ^
Error: PHP Parse error: syntax error, unexpected '"' in (program) on line 2
[email protected] /tmp $ nano test.php
[email protected] /tmp $ cat test.php
<?php
# Changed to "normal" function call:
include_once("foo.php");
[email protected] /tmp $ uniter2ast test.php 
{
    "statements": [
        {
            "expression": {
                "func": {
                    "string": "include_once",
                    "name": "N_STRING"
                },
                "args": [
                    {
                        "string": "foo.php",
                        "name": "N_STRING_LITERAL"
                    }
                ],
                "name": "N_FUNCTION_CALL"
            },
            "name": "N_EXPRESSION_STATEMENT"
        }
    ],
    "name": "N_PROGRAM"
}

This should explain itself. :)

Non-numeric PHP arrays passed to JS should be converted to Objects

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });
php.getStderr().on('data', function (text) { console.error(text); });

var jsFunction = function(data) {
        console.log(data);
}

php.expose(jsFunction, 'jsFunction');
php.execute('<?php $x = array("A" => "B"); $jsFunction($x);');

[ A: 'B' ]

runtime.pausable is undefined

So, I basically have the prototype of my plugin done.

However, there is an issue I do not quite comprehend...

/Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:4438
                    pausable = runtime.pausable,
                                      ^

TypeError: Cannot read property 'pausable' of undefined
    at _.extend.compile (/Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:4438:36)
    at Object.<anonymous> (/Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:51:16)
    at __webpack_require__ (/Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:20:30)
    at /Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:40:18
    at Object.<anonymous> (/Users/Ingwie/Work/uniter/uniter-webpack/testcase/out/bundle.js:43:10)
    at Module._compile (module.js:398:26)
    at Object.Module._extensions..js (module.js:405:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Function.Module.runMain (module.js:430:10)

That maps to:

    _.extend(Runtime.prototype, {
        compile: function (wrapper) {
            var runtime = this,
                pausable = runtime.pausable, // <---- This line.
                phpCommon = runtime.phpCommon;

            return function (options, environment) {

Which is phpcore/src/Runtime.js.

Now, this is the code that bootstraps phpruntime:

var phpRuntime = require("phpruntime/sync");
var phpEnv = phpRuntime.createEnvironment();
var _ = require("microdash");

// Set up defaults...
var stdout = phpEnv.getStdout();
var stderr = phpEnv.getStderr();

// # I/O for console. STDIN is unused by default.
stdout.on("data",function(str){
    console.log(str);
});
stderr.on("data",function(str){
    console.error(str);
});

// Simply add some options.
function makeOptions(userOpt) {
    var baseOpt = phpEnv.getOptions();
    var targetObj = _.extend({}, baseOpt, userOpt);
    return targetObj;
}

module.exports = {
    makeOptions: makeOptions,
    getEnvironment: function(){ return phpEnv; }
};

And this is the actual "executor":

/* 0 */
/***/ function(module, exports, __webpack_require__) {

        // Imports
        var compile = __webpack_require__(/* phpruntime/sync */1).compile;
        var $ENV = __webpack_require__(/* lib/Runtime.js */87); // This is the script from above!
        // Build the context
        var makeCtx = compile(function(stdin, stdout, stderr, tools, namespace) {
            var namespaceScope = tools.createNamespaceScope(namespace),
                namespaceResult, scope = tools.globalScope,
                currentClass = null;
            stdout.write(tools.valueFactory.createString("Hello, world!").coerceToString().getNative());
            tools.valueFactory.coerce(__webpack_require__(/* demo2.php */88));
            return tools.valueFactory.createNull();
        });

        var options = $ENV.makeOptions({
            path: "<internal: " + module.id + ">"
        });

        var ctx = makeCtx(options, $ENV.getEnvironment());
        // Expose the exporter object
        // If the underlying code would assign a primitive, we can\'t assign properties to it.
        // So we make an abstraction!
        var exp1 = {},
            exp2;
        ctx.expose(exp1, "exports");
        exp2 = new Object(exp2);
        // Run and return.
        var rt = ctx.execute();
        Object.defineProperty(exp2, "__phpReturn", {
            value: rt,
            variable: false
        });
        Object.defineProperty(exp2, "__phpExports", {
            value: exp1,
            variable: false
        });
        module.exports = exp2;

/***/ },

Did I miss something, maybe? o.o

REPL

So for time, i really wanted to just try out functions. To do so, I did something like

nano file.php
uniter2js file.php > file.php.js
node -p "require('./file.php.js')().execute()"

but, this is rather a no-go :) It works but is hacky.

For another language-oriented project, for OJ, I wrote a little REPL: https://github.com/IngwiePhoenix/oj-node/blob/master/bin/oj

In order to write one for Uniter, it would need the ability to compile/run fragments - like: $foo = 20; echo $foo; - without a <?php, and in the same context as the previous statement.

You probably can base your REPL on what I did with OJ there. Its the most simple REPL you can do, really. :)

Heredoc/Nowdoc

<?php
$txt = <<<EOT
bar
EOT;

and

<?php
$name = "Bob";
$txt = <<<-'EOT'
{$name}'s bar
EOT;

dont work. :)

Just a heads up! I might got the second - Nowdoc - wrong. But you probably know what I meant...

sizeof/count is not implemented

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });
php.getStderr().on('data', function (text) { console.error(text); });

php.execute('<?php print sizeof([]);');

PHP Fatal error: Call to undefined function sizeof()

Non-numeric JS arrays not properly handled

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });

var jsFunction = function () {
    var arr = [];
    arr['bobs'] = 'burgers';
    return arr;
};

php.expose(jsFunction, 'jsFunction');

php.execute('<?php print var_dump($jsFunction());');

array(0) {
}

...error: Call to undefined method JSObject::__toString()

Actually, this is not quite right. Each JavaScript object has a .toString() method.

> typeof Object.toString
'function'
> typeof Number.toString
'function'
> typeof "abc".toString
'function'
> typeof (1).toString
'function'
> typeof (1.2).toString
'function'
> typeof /.+/.toString
'function'

Suggestion: JSObject::__toString() should map 1:1 to (...).toString().

get_class_methods is not implemented

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });
php.getStderr().on('data', function (text) { console.error(text); });

php.execute('<?php print get_class_methods();');

PHP Fatal error: Call to undefined function get_class_methods()

Versions are off

I was experiencing weird behavior, as explained in Gitter. So I decided to whipe my node_modules and re-install.

Surprisingly, this came out:

/Users/Ingwie/Work
โ”œโ”€โ”ฌ [email protected] 
โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”œโ”€โ”ฌ [email protected] 
โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”œโ”€โ”ฌ [email protected] 
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”ฌ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ [email protected] 
โ”‚ โ”‚ โ”‚ โ””โ”€โ”ฌ [email protected] 
โ”‚ โ”‚ โ”‚   โ””โ”€โ”€ [email protected] 
โ”‚ โ”‚ โ””โ”€โ”€ [email protected] 
โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ””โ”€โ”€ [email protected] 
โ”œโ”€โ”ฌ [email protected] 
โ”‚ โ”œโ”€โ”€ [email protected] 
โ”‚ โ””โ”€โ”€ [email protected] 
โ””โ”€โ”ฌ [email protected] 
  โ”œโ”€โ”€ [email protected] 
  โ”œโ”€โ”ฌ [email protected] 
  โ”‚ โ””โ”€โ”€ [email protected] 
  โ””โ”€โ”€ [email protected] 

You see how the various parts use various versions? this is prune to error. I could sit down and test/update the parts myself, since i am in the Uniter org. :) but i just wanted to ask for permission first.

Add asynchronous API

Uniter needs support for an internal asynchronous API, so PHP execution may be blocked while an asynchronous operation completes, eg. for sleep(...) or usleep(...) PHP functions or a require(...) that needs to perform AJAX to fetch the included file.

Define contextual/lexical constants

Make sure that all the PHP constants exist:

<?php
namespace UniterPHP\Test {
    class ConstantTests {
        static function run() {
            echo "Namespace: ".__NAMESPACE__."\n";
            echo "Class: ".__CLASS__."\n";
            echo "Function: ".__FUNCTION__."\n";
            echo "File: ".__FILE__."\n";
            echo "Dir: ".__DIR__."\n";
            echo "Line: ".__LINE__."\n";
            echo "PHP_EOL: [".(PHP_EOL != "\n" ? "Yes":"No")."]\n";
        }
    }
    ConstantTests::run();
}

In addition, the test for PHP_EOL always prints [1].

More Root Level Functions

Hi @asmblah ,

I'd like to add new functions to the Root Level of "Uniterland" Apps. I'd be happy to begin with something along the lines of #24 , as I have a couple of good remote Node Devs who I'd quite like to task with some extensions and integrations for this (and tests :) ). I'd quite like to use the defined method and so I'd like to start there.

I'd also like to be able to use Locutus, http://locutus.io/php/ . They've got 300+ functions which have proven useful to us so far. The only issue there is that we use a wrapper class and access these methods like

$current_time =  $php->time() ;

Where it would be more PHP like to be able to include that whole library of methods at root level so we could do...

$current_time =  time() ;

So, would there be a way of having a if function exists in PHPRuntime, use it. Otherwise look in xyz Objects for those functions? Then i could include that PHP/Locutus object/variable there and use those functions at root level. I could also write something like "Issue24Class", that includes those functions mentioned in that issue, including defined.

Would that be possible? If so, could you point in the direction of how to do it please? Hopefully we can put those functions into a PR once they're done. It's fine to use all the Node Jargon in describing it, as its likely to be Node guys implementing this one.

Thanks, Dave

Add execution timeout

Infinite loops in the browser can lock up the UI so are a big no-no. Uniter should support the set_time_limit(...) function in order to handle this scenario.

The default of 30 seconds in the Zend engine is too high for in the browser: a default of 1 second should be enforced there.

define(...) and NodeJS

https://www.npmjs.com/package/amdefine

You might be interested in this since I see that Uniter still uses define(). I also am suggesting this because of NodeJS. Having it require the dist/uniter.js file loads a lot of overhead and could, in one way or another, confuse the system.

Changing index.js to use require("amdefine/intercept") and then module.exports = require("./js/main") will enable NodeJS to properly load the modules without the confusion of the browserify bundle.

Passing a value back to JS from PHP that was passed from JS gets corrupted

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });
php.getStderr().on('data', function (text) { console.error(text); });

var jsFunction = function() {
        return {
                "a": "b"
        };
}

var jsFunction2 = function(data) {
        console.log(data);
}

php.expose(jsFunction, 'jsFunction');
php.expose(jsFunction2, 'jsFunction2');
php.execute('<?php $x = $jsFunction(); $jsFunction2($x);');

{ objectValue: 
   { factory: 
      { nextObjectID: 4,
        callStack: [Object],
        globalNamespace: [Object],
        pausable: [Object] },
     callStack: { calls: [Object], stderr: [Object] },
     type: 'object',
     value: { a: 'b' },
     classObject: 
      { callStack: [Object],
        constants: undefined,
        constructorName: null,
        interfaceNames: [],
        InternalClass: [Function: JSObject],
        name: 'JSObject',
        namespaceScope: undefined,
        staticProperties: {},
        superClass: undefined,
        valueFactory: [Object] },
     id: 3,
     pointer: 0,
     properties: {} },
  pausable: 
   { transpiler: 
      { expressionTranspiler: [Object],
        statementTranspiler: [Object] } },
  valueFactory: 
   { nextObjectID: 4,
     callStack: { calls: [Object], stderr: [Object] },
     globalNamespace: 
      { callStack: [Object],
        children: {},
        classAutoloader: [Object],
        classes: [Object],
        constants: [Object],
        functionFactory: [Object],
        functions: [Object],
        name: '',
        namespaceFactory: [Object],
        parent: null,
        valueFactory: [Circular] },
     pausable: { transpiler: [Object] } } }

Inlined comparsion showcases weird behaviour

Within my last test cases, I used this line:

echo "PHP_EOL: [".(PHP_EOL == "\n" ? "Yes" : "No")."]";

... but actually, it would print

PHP_EOL: []

Obviously, that would only be the case if the expression would only be evaluated by its first half:

0 -> Number(0) -> String("")
1 -> Number(1) -> String("1")

That, at least, would totally adhere to the PHP way of handling this.

However, replacing the above expression with the following, yields a totally different result:

$hasEol = (PHP_EOL == "\n");
echo "PHP_EOL: [".($hasEol ? "Yes":"No")."]\n";

In this one, the output actually reads

PHP_EOL: [Yes]

Note: This time, the second half of the inlined if-statement was evaluated correctly.

if(true) String("Yes")
else String("No")

...whilst in the first expression, the bool was converted directly into a string-ish expression.

This behaviour was encountered totally randomly, but it might be something to investigate. :)

Inherited constructor is not called on instantiation

var php = require('uniter').createEngine('PHP');
php.getStdout().on('data', function (text) { console.log(text); });
php.getStderr().on('data', function (text) { console.error(text); });

php.execute('<?php class A { function __construct() { print "There "; } } class B extends A {} print "Hello "; new B(); print "World ";');

Hello 
World 

Superglobals

While looking at this project: https://github.com/js-cookie/js-cookie
... i realized, Uniter never actually seemed to introduce superglobals.

So I would like to ask, how one would set up the superglobals? Say I want to emulate $COOKIE with this project above, that would mean I'd need:

  • to setup $COOKIE superglobal.
  • add cookie_*() functions.
  • probably add getter and setter for array-key access. (offsetGet kinda stuff).

I can see this being implemented as part of my uniter-stl, which I will likely re-write soon to adapt a bit better and to a nicer structure. But for this to work in general, I'd still need to know how to setup a superglobal, and especially with a way to add getter and setter.

The other superglobals are:

  • $_SERVER
  • $_ENV
  • $_GET
  • $_POST
  • $_REQUEST
  • $_FILES
  • $_SESSION
  • $argv / $argc

I could see these being implemented by making them all be raw associative arrays - aka. empty JS objects - by default - which they really are - and giving the user an API to set their value.

// For example:
phpruntime.setSuper("$_SERVER", {HTTP_CONTENT_TYPE: "text/plain"});
phpruntime.setSuper("$_ENV", process.env);
phpruntime.setSuper("$argv", process.argv || ["--some", "arg"]);

A pseudo testcase (jasmine/expect):

var _ = require("microdash");

var object_globals = [
    "$_GET", "$_POST", "$_REQUEST", "$_FILES",
    "$_SERVER", "$_ENV", "$_SESSION"
];

describe("PHPRuntime", function(){
    describe(".setSuper", function(){
        object_globals.forEach(function(globalName){
            it("only accepts an object for: "+globalName, function(){
                var phpruntime = this.phpruntime; // get php runtime
                expect(function() {
                    phpruntime.setSuper(globalName, {foo: "bar"});
                }).not.toThrow; // a TypeErrpr exception
            });
        });

        it("should only accept an array for $argv", function(){
            var phpruntime = this.phpruntime;
            expect(function(){
                phpruntime.setSuper("$argv", {});
            }).toThrow;
            expect(function(){
                phpruntime.setSuper("$argv", []);
            }).not.toThrow;
        });

        it("should automatically match $argc to $argv", function(){
            var phpruntime = this.phpruntime;
            var argv = ["uniter", "foo.php", "--arg1", "--arg2=withSomething"];

            phpruntime.setSuper("$argv", argv);

            expect(phpruntime.getSuper("$argc").length).isEqual(argv.length);
        });

        it("should prevent manipulation of $argc", function(){
            var phpruntime = this.phpruntime;
            expect(function(){
                phpruntime.setSuper("$argc", /* it's over */9000);
            }).toThrow;
        });
    });
});

( Yup, im geting used to unit testing right there! :) )

Enhancement to the include transport

So originally, the function that returns/resolves the result of an include or require statement was supposed to be either a wrapper function for the new compiled PHP code or the already compiled code.

Since we are urging the compiler itself out of the runtime, we will only be able to return the transpield PHP source as returned by phpToJS. However, what if we want to actually return a value itself? Say I want to tweak the require keyword to return node modules, I'd like to do something such as

include: function(path, promise) {
    // ... snip ...
    var module = require(...);
    promise.resolve( ValueFactory.createValue(module) );
}

I believe this should be possible.

Defining extensions

PHP usually provides extensions; like pcntl, hprose, sockets and alike.

In order to reproduce these within Uniter, this is what I would suggest:

var Extension = require("uniter-extension");

var cURL = new Extension();

cURL.function("curl_init", function(){
    // Implement curl_init
});

function cURLClass(args) {}
    // a cURL class
}
cURL.addClass("cURL", cURLClass);

module.exports = cURL;

Now, we could extend Uniter by an additional function. Currently, we have .install() to install functions, constants and classes into the system. As far as I remember, they all go into global scope. Extensions could be registered similarily:

var rt = require("phpruntime");
rt.registerExtension( "curl", require("./myCurlExt.js") );
rt.loadExtension("curl");

This would also provide the extension_loaded function to Uniter. Also, we might be able to use dl().

To summarize:

  • Add Extension support to Uniter, so that native PHP extensions can be emulated.
  • Let the runtime know about Extensions. The user should be able to add, remove, enable and disable extensions.
  • Uniter PHP scripts should be able to use:
    • extension_loaded($extName)
    • dl($extName)

Implementation is up to you, the above are just pseudo-code examples :) But i bet you get what I meant.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.