Spesso mi capita di dover testare delle librerie o il funzionamento particolare di alcune funzioni in determinate circostanze. Solitamente creo il mio file PHP e lo faccio eseguire dal mio webserver e poi controllo il risultato. Pratica comune, direi, ma da quando ho iniziato ad utilizzare Python mi trovo spesso a rimpiangere la sua comoda command line.
Stamattina, visto che avevo qualche minuto di tempo, mi sono messo con il mio iBook alla mano ed ho creato un semplice script che emula una shell per PHP.
Lo script è molto semplice e sfrutta la funzione eval per valutare l'espressione immessa dall'utente. Purtroppo soffre di alcuni problemi (lascio a qualcun altro il compito di risolverli) dovuti al motore di PHP o a piccole inesattezze che possono essere corrette facilmente. La shell accetta in input un'espressione valida PHP, la esegue e restituisce in output il valore restituito dall'espressione. In caso vegano scritti costrutti sintattici particolari, quali classi, cicli, istruzioni condizionali, la shell si occupa di immagazzinare in memoria l'intero blocco di istruzioni e di eseguirlo. La shell indenta automaticamente il codice se trova una parentesi graffa alla fine della riga, quindi è necessario seguire delle semplici regole sintattiche per far funzionare correttamente la shell. Siete liberi di modificare e correggere il codice.
<?php
final class shell
{
private static function readline($prompt = "")
{
echo $prompt;
$o = $c = "";
while (($c != "\r") and ($c != "\n"))
{
$o.= $c;
$c = fread(STDIN, 1);
}
fgetc(STDIN);
return $o;
}
public static function read_input()
{
$depth = 0;
$mode = 0;
$count = 0;
$buffer = "";
do
{
$line = shell::readline($depth == 0 ? ">>> " : ("...
".implode("", array_fill(0, $depth, "\t"))));
$t_line = trim($line);
if(substr($t_line, -1) == '{')
{
++$depth;
$mode = 1;
if($t_line{0} == '}')
{
--$depth;
}
}else if(preg_match("/if|while|else|switch|declare/i", $t_line))
{
++$depth;
$mode = 0;
}else
{
if($depth > 0)
{
if($mode == 1)
{
if($t_line{0} == '}')
{
--$depth;
}
}else
{
--$depth;
}
}
}
$buffer .= " ".$line;
++$count;
}while($depth > 0);
return array($buffer, $count > 1);
}
}
ob_implicit_flush(true);
error_reporting(E_ALL ^ E_NOTICE);
ini_set('html_errors', 0);
printf("PHP %s (%s) [%s]\n", phpversion(), php_sapi_name(), PHP_OS);
while(true)
{
list($line, $multiline) = shell::read_input();
if($line === false)
{
echo "\n";
break;
}
if(strlen($line) == 0)
{
continue;
}
if(!$multiline)
{
$line = 'return '.$line;
}
ob_start();
$ret = @eval('unset($line); '.$line.';');
if(ob_get_length() == 0)
{
switch(true)
{
case is_bool($ret):
echo $ret ? 'true' : 'false';
if(!$ret)
{
echo " -- probably an error while evaluating entered code.";
}
break;
case is_string($ret):
echo "'" . addcslashes($ret, "\0..\37\177..\377") . "'";
break;
case !is_null($ret):
print_r($ret);
break;
}
}
unset($ret);
$out = ob_get_contents();
ob_end_clean();
if((strlen($out) > 0) and (substr($out, -1) != "\n"))
{
$out .= "\n";
}
echo $out;
unset($out);
}
?>
Lo script deve essere eseguito con la versione CLI di PHP5 ma con pochi accorgimenti può essere reso compatibile con PHP4. Come potete notare non è nulla di complicato, ma cosa potete aspettarvi da pochi minuti di lavoro ?