#! /usr/bin/php
<?php
/**
 * Class used to isolate functions and vars that needs to keep defined while evaluating users code
 */
class PHPc{

    const 
PROMPT 'php>';
    const 
PSIZE 5;//Prompt's size +1

    
public static $lines = array();
    public static 
$code_to_eval;
    public static function 
readChar(){
        
$ret exec('stty cbreak -echo;c=`dd bs=1 count=1 2>/dev/null`;stty -cbreak echo;echo "$c."');
        if(
$ret=='.') return "\n";
        return 
substr($ret01);
    }
    public static function 
delChar(&$line$pos){
        
$line substr($line0$pos).substr($line$pos+1);
    }
    public static function 
setLine(&$from$to){
        
$size strlen($from);
        
$goto strlen($to)+self::PSIZE;
        echo 
"\033[".self::PSIZE."G\033[{$size}P{$to}\033[{$goto}G";
        
$from $to;
        return 
$goto;
    }
}
echo <<<INTRO
PHP Command 0.9 Created by Hugo Vacher (korri.fr)

Code is interpreted when line end with ';' or '}' and that all '{' are closed.

You are free to use, modify and redistribute this code, please leave me a comment if you do it.

INTRO;

    while(
true){
        
PHPc::$code_to_eval '';
        
$continue true;
        while(
$continue){
            
//Reset history index
            
$index 0;
            
//Say that line isn't finished
            
$ln true;
            
//Reset cursor pos
            
$c PHPc::PSIZE;
            
$total PHPc::PSIZE;

            
$line '';
            echo 
PHPc::PROMPT;
            
//Add empty line
            
array_unshift(PHPc::$lines'');

            while(
$ln){
                
$char PHPc::readChar();
                
$cod ord($char);

                
//Gonna parse each key
                
if($cod == 10){//Enter
                    
echo "\033[".PHPc::PSIZE."G\n";
                    
$ln false;
                }else if(
$cod == 127){//Ret arriere
                    
if($c PHPc::PSIZE){
                        
PHPc::delChar($line, --$c-PHPc::PSIZE);
                        --
$total;
                        echo 
"\033[D\033[1P";
                    }
                }else if(
$cod == 4){//Ctrl + D
                    
break 3;//We exit programm
                
}else if($cod == 27){
                    
$key PHPc::readChar().PHPc::readChar();
                    switch(
$key){
                        case 
'[3'://Del
                            
PHPc::readChar();
                            if(
$c $total){
                                
PHPc::delChar($line$c-PHPc::PSIZE);
                                --
$total;
                                echo 
"\033[1P";
                            }
                            break;
                        case 
'[A'://up
                            
if(isset(PHPc::$lines[$index+1])){
                                if(
$index==0PHPc::$lines[0] = $line;
                                
$to PHPc::$lines[++$index];
                                
$c PHPc::setLine($line$to);
                                
$total $c;
                            }
                            break;
                        case 
'[B'://down
                            
if($index 0){
                                
$to PHPc::$lines[--$index];
                                
$c PHPc::setLine($line$to);
                                
$total $c;
                            }
                            break;
                        case 
'[D'://left
                            
if($c PHPc::PSIZE) echo "\033[".--$c.'G';
                            break;
                        case 
'[C'://right
                            
if($c<$total) echo "\033[".++$c.'G';
                            break;
                        default:
                            
//We don't do nothing, cause all keys like F1-12 don't needs to have an effect
                    
}
                }else{
                    echo 
"\033[4h$char\033[4l";//Echo char in insert mode
                    
$line substr($line0$c PHPc::PSIZE).$char.substr($line$c PHPc::PSIZE);
                    ++
$c; ++$total;
                }
            }
            
PHPc::$lines[0] = $line;
            
PHPc::$code_to_eval .= $line;
            if(
substr($line,-1)==';' || substr($line,-1)=='}'){
                
$cnto substr_count(PHPc::$code_to_eval'{');
                
$cntc substr_count(PHPc::$code_to_eval'}');

                if(
$cntc >= $cnto){
                    
$continue false;
                }
            }
        }
        
//Clear local vars
        
unset($c$total$line$continue$char$cod$cnto$cntc$to$index);

        
ob_start();
        eval(
PHPc::$code_to_eval);
        
$txt ob_get_contents();
        
ob_end_flush();
        if(
$txt!='' && substr($txt, -1) != "\n") echo "\n";
    }
    echo 
"\nExit\n";
?>