iphp icon indicating copy to clipboard operation
iphp copied to clipboard

Add support for closures, loops, classes in REPL

Open scottdavis opened this issue 16 years ago • 21 comments

This was bugging me for awhile and i hacked away for about 2.5 hours trying to figure out a work about but example:

> $foo = function() {return 'bar';}
> 
Uncaught exception with command:
Fatal error executing php: PHP Fatal error:  Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo:18
Stack trace:
#0 /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo(18): serialize(Array)
#1 {main}
  thrown in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo on line 18

Exception: Serialization of 'Closure' is not allowed in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo on line 18

Call Stack:
    0.0004     635040   1. {main}() /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo:0
    0.0009     637576   2. serialize() /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.U3N9Oo:18

Same goes for defining functions and classes function:


> function foo() {return 'bar';}
> 
Uncaught exception with command:
Fatal error executing php: PHP Parse error:  syntax error, unexpected T_STRING, expecting '(' in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.8CUHcL on line 12

Parse error: syntax error, unexpected T_STRING, expecting '(' in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.8CUHcL on line 12
> 

Classes:

> class Foo {}
> 
Uncaught exception with command:
Fatal error executing php: PHP Parse error:  syntax error, unexpected T_CLASS in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.Pk4qgL on line 12

Parse error: syntax error, unexpected T_CLASS in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.Pk4qgL on line 12
> 

While i had a work around working the storing of $_ and $__out are not possible when you create a a special case even "foreach" will explode using serialization

foreach(range(0,10) as $i) { echo $i; }
> 
Uncaught exception with command:
Fatal error executing php: PHP Parse error:  syntax error, unexpected T_FOREACH in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.mXsYfJ on line 12

Parse error: syntax error, unexpected T_FOREACH in /private/var/folders/oQ/oQhij0hDFVOxzm9dMI9EC++++TI/-Tmp-/iphp.command.mXsYfJ on line 12
> 

Solution is avoid serialization and store the code input unserialized in a file and keep appending to it and re-executing, As for returning output some string parsing may be in order to find variables and var_dump-ing them may be in order or just rely or just print tokenize the last returnable line;

edit: fixed typo's my brain is fried

scottdavis avatar Dec 10 '09 04:12 scottdavis

wow interesting problem, I hadn't even contemplated trying to do this type of thing. but clearly it's cool!

Did you get to a solution? I am too tired to think about it myself atm :)

apinstein avatar Dec 10 '09 04:12 apinstein

maybe we need some special multi-line mode?

apinstein avatar Dec 10 '09 04:12 apinstein

I had it working sort of kind of but them that way would break when it was serialized so multi-line mode = no serialization of anything

The real problem isn't getting a multi-line mode working its this block of code

ob_start();
\$_ = {$command};
\$__out = ob_get_contents();
ob_end_clean();

Sticking "class Foo {}" into the $_ = class Foo {} throws a syntax error

scottdavis avatar Dec 10 '09 04:12 scottdavis

Wow yeah so irb lets you write any ruby. no wonder they have "reload"!

I think we could accomplish that with some kind of "eval vs define" mode.

Currently we have eval mode, but we could have certain keywords trigger "define" mode, like "class, interface, function, etc".

Then store that data separately in a single "multiline_require" file that would be included just before the $_ = {$command}

Does that sound promising?

apinstein avatar Dec 10 '09 05:12 apinstein

Actually maybe this is an ideal situation to use the built-in php tokenizer:

http://us2.php.net/manual/en/function.token-get-all.php

Pre-parse the entered line and see what it looks like. No idea how it deals with syntax errors.

apinstein avatar Dec 10 '09 05:12 apinstein

well you run php_check_syntax($file) then you tokenize it ill prototype something tomorrow and post it in a branch

scottdavis avatar Dec 10 '09 05:12 scottdavis

and the way i was doing it was running this huge regex i made "/^(class|function|interface|foreach|etc)/" your above solution should solve the problem for classes but foreach and inline block operators will need even more special care

scottdavis avatar Dec 10 '09 05:12 scottdavis

php_check_syntax is deprecated/obselete.

based on the way irb looks like it works, I think we just have to keep track of what's being entered. When you hit ENTER look at the line for quotes, foreach, etc and maintain state... then keep a single file with all of these commands and keep appending them. they'd have to be run each time in the repl exec tho which could get slow for certain things (and certianly not be idempotent either).

apinstein avatar Dec 10 '09 05:12 apinstein

the way i had it working for classes was it detected the { operator and } operator and tracked nested operators so making

class Foo {
 public function __construct() {

 }
}

Had a nested of index of 2 and stored each entered line in and array until the last } was captured setting the nested counter to zero then i was injecting them into the doCommand() as a array imploded on "\n"

scottdavis avatar Dec 10 '09 05:12 scottdavis

it's def a start, but I think too primitive. it'd be ideal to follow the PHP parsing rules by using token_get_all(), then we won't have to educate people on how to hack iphp to enable multi-line stuff...

apinstein avatar Dec 10 '09 05:12 apinstein

started working on a tokenizer class http://github.com/jetviper21/iphp/tree/b282e9ccc3d17ca333db0e807d980899f9727c0e

scottdavis avatar Dec 10 '09 07:12 scottdavis

cool! looks promising.

I think it might be wiser to have more than just a "multi-line" boolean; rather use a state stack which effects what action happens on RETURN.

For instance, "" will just let the NL through since NL is a valid char for quoted strings. But certain keywords or blocks will enter multi-line mode, and who knows if we'll need different ones.

We can just maintain an internal stack of "inputMode" with options like MODE_QUOTED_STRING, MODE_BLOCK, etc as needed, and then each mode can look for its "close" tag.

Make sense?

apinstein avatar Dec 10 '09 15:12 apinstein

well i created the boolean to run a test from inside doCommand if that passes i was going to write a buffering class (that implements the state stack) that can handle the multi line input and store it until its valid executable syntax i see no need ot crud up the main class with this

scottdavis avatar Dec 10 '09 17:12 scottdavis

Yeah i like the idea of a buffering class... could be used to do multi-line php input in any project (although not sure how big of a need that is...)

apinstein avatar Dec 10 '09 17:12 apinstein

Few things as im hacking through this im working on the doCommand function and was wondering why eval will not work for the main code execution so we don't need to use tempfiles?

scottdavis avatar Dec 10 '09 21:12 scottdavis

that's how it used to work but there is a big problem with that. The "host" of eval() dies if there's a fatal error.

Thus if you did something like $a->foo() you'd get a FATAL error that would kill the shell.

IMO the "fatal-safety" of iphp is the killer feature it has; I'd never seen it done anywhere else before and this problem severely limited the utility of the shell for me since it would die after you'd built up state because of a simple typo.

apinstein avatar Dec 10 '09 22:12 apinstein

totally understand i didn't know you couldn't try catch an eval

scottdavis avatar Dec 10 '09 23:12 scottdavis

just finished moving some stuff out and writing some tests going to start working on test cases for the main class so i can build out a multi line function without breaking everything

scottdavis avatar Dec 11 '09 02:12 scottdavis

i have made a bunch of mods; please rebase your feature branch on my master, thanks!

apinstein avatar Dec 11 '09 21:12 apinstein

i have already merged in most of bermi's changes ill get around to pulling yours down tomorrow

scottdavis avatar Dec 12 '09 05:12 scottdavis

Scott not sure if you're working on this any more, but I did hook up iphp to token_get_all() tonight and managed to get iphp working gracefully when the "command" doesn't return data.

I would be very interested in your multiline stuff now...

apinstein avatar Nov 12 '11 03:11 apinstein