Add support for closures, loops, classes in REPL
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
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 :)
maybe we need some special multi-line mode?
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
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?
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.
well you run php_check_syntax($file) then you tokenize it ill prototype something tomorrow and post it in a branch
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
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).
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"
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...
started working on a tokenizer class http://github.com/jetviper21/iphp/tree/b282e9ccc3d17ca333db0e807d980899f9727c0e
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?
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
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...)
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?
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.
totally understand i didn't know you couldn't try catch an eval
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
i have made a bunch of mods; please rebase your feature branch on my master, thanks!
i have already merged in most of bermi's changes ill get around to pulling yours down tomorrow
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...