Location: PHPKode > scripts > Console App > console-app/Console_app.php
<?php
#!/usr/bin/php
/**
* @package console
* @since 2005-04-20
* @author hide@address.com
* @licence LGPL
* @changelog - 2006-08-17 - new parameter $dfltIsYes for method msg_confirm()
*            - 2006-05-10 - progress_bar() and refresh_progress_bar() now support array(msg,tag) as $msg parameter
*            - 2006-05-04 - new parameters $styles and $return for console_app::print_table()
*            - 2006-05-02 - now read will return false on CTRL+D if property _captureEOT is FALSE 
*            - 2006-04-26 - call of console_app::read() as a static method will now autodetect and use the readline extension
*            - 2006-04-23 - now console_app::read() and console_app::msg_	read() even as a static method
*            - 2006-04-16 - now console_app::get_arg() can also retrieve unknown_args ($console_app->get_arg(0))
*            - 2006-04-15 - now auto add realine history if readline enable.
*                         - new parameter _captureEOT to capture EndOfTransmission signal (CTRL+d)
*            - 2006-04-14 - new property console_app::_lnOnRead to remove the automaticly added new line on read msg.
*                         - new methods progress_bar() and refresh_progress_bar()
*            - 2006-04-13 - now you can use "\n" in args descriptions
*                         - new method print_table() for table rendering
*            - 2006-02-08 - remove some E_NOTICE on previously added readline support 
*            - 2006-02-03 - now can read multiple SHORT FLAG at once (ie: -AZEXC instead of -A -Z -E -X -C )
*            - 2006-01-30 - some visual amelioration on the display_help
*            - 2006-01-12 - new msg_read method
*            - 2005-12-29 - now use STDIN/STDOUT/STDERR and add a fancyerror parameter 
* @example
* @code
# set the app
$app = &new console_app();
$app->set_app_desc("This is a sample console app.");
# set parameters
$app->define_arg('name','n','','your name');

# don't forget to parse the command line to get args
$app->parse_args();

# get the value of 'name' like this
$name = $app->get_arg('name');
if( $name =='' )
  $name = $app->read("enter your name");

# display a hello msg
$app->msg("Hello $name",'blue'); 
@endcode
@todo add better support for numeric args to permit to define some help for them too and know them at all
@todo better table support
*/


class console_app{
  /** args known by this class */
  var $known_args = array();
  /** flags known by this class */
  var $known_flags = array();
  /** unknown given args */
  var $unknown_args = array();
  /** setted args on command line */
  var $setted_args = array();
  /** setted flags on command line */
  var $setted_flags = array();
  var $app_desc = '';
	var $required_args;
	var $required_flags;
	/** is there a readline extension available or not */
  var $_readline = FALSE; 
	var $_lnOnRead = TRUE;
	var $_captureEOT = TRUE; # exit on EndOfTransmission (CTRL+d)
	/** Manage history file Require readline extension */
	var $_history_file = FALSE;
	function console_app(){
		$this->_readline = function_exists('readline');
		# $this->workingdir   = getcwd();
		# $this->appdir       = dirname($_SERVER['PHP_SELF']);
	}
	# function set_working_dir($dir=null){
		# switch($dir){
			# case null:  $dir = $this->workingdir; break;
			# case '@pp': $dir = $this->appdir; break;
		# }
		# chdir($dir);
	# }
  function get_arg($longname){
    # try args
    if( isset($this->setted_args[$longname]) )
      return $this->setted_args[$longname];
    if( isset($this->setted_flags[$longname]) )
      return $this->setted_flags[$longname];
    if( isset($this->unknown_args[$longname]) )
      return $this->unknown_args[$longname];
    return FALSE;
  }
  function get_args(){
    return array_merge($this->setted_args,$this->setted_flags,$this->unknown_args);
  }
  /**
  * define the flags the programm will wait for
  * @param string $flagname the name of the flag (so --flagname will work)
  * @param mixed  $sf       the short flag to use can be a string or an array
  *                         if an array is used then the 1st value will set flag to true and the 2d to false
  *                         if so a --no-flagname will be created to
  * @param bool  $dflt      the default value for this flag leave null if this is a required flag 
  *                         (DON'T USE 'unset' ANYMORE to leave null a flag without setting him as required, SET IT TO FALSE will do the same)
  * @param string $desc      description for the help screen
  */
  function define_flag($flagname,$sf=null,$dflt=null,$desc='** no description available **'){
    $this->known_flags['--'.$flagname] = $flagname;
    # $this->flags[$flagname]['longname']= $name;
    $this->flags[$flagname]['desc'] = $desc;
    if($dflt==='unset') $dflt=FALSE; # this line is just here to handle old silly 'unset' value for old prog
		
		# check for default value or set it as required
		if( is_null($dflt) )
      $this->required_flags[]  = $flagname;
    else
      $this->setted_flags[$flagname] = $this->flags[$flagname]['dflt'] = (bool) $dflt;
    
		# check short tags
    if($sf){
      if(is_array($sf))
        list($sf,$usf) = $sf;
      $this->known_flags['-'.$sf] = $flagname;
    }
    
		# define optionnal unflag
    if(isset($usf))
      $this->define_unflag($flagname,"no-$flagname",$usf);
  }
  /**
  * set a unflag flag for$flagname
  * @param string $flagname         flagname to set an unflag for
  * @param string $unflagname       the unflag longname 
  * @param string $shortunflagname  the short unflag name
  */
  function define_unflag($flagname,$unflagname=null,$shortunflagname=null){
    if(! isset($this->flags[$flagname])){
      $this->msg_error('unflag set for non existing flag: '.$flagname);
      return FALSE;
    }
    if(! is_null($unflagname))
      $this->unflags['--'.$unflagname] = $flagname;
    if(! is_null($shortunflagname))
      $this->unflags['-'.$shortunflagname] = $flagname;
    return TRUE;
  }
  function set_flag($flag,$value=TRUE){
    # try in known_flags
    if( isset($this->knwon_flags[$flag]) ){
      $this->setted_flags[$this->knwon_flags[$flag]] = $value;
      return TRUE;
    }
    # try in unflags
    if( isset($this->unflags[$flag]) ){
      $this->setted_flags[$this->unflags[$flag]] =(bool) (! $value);
      return TRUE;
    }
    #try as flagname
    if(isset($this->flags[$flag])){
      $this->setted_flags[$flag] = $value;
      return TRUE;
    }
    $this->msg_error('Try to set an unknwon flag: '.$flag);
  }
  /**
  * set possible arg for this application
  * @param str    $longname    this is the name you will use to access this arg inside your application.
  *                            the program may so have a --longname argument
  * @param str    $shortname   set an optionnal short name for arg, so your app will take a -S arg (S for shortname :))
  * @param str    $default     optionnal default value to set this arg to, if not passed on the command line
	*														 Setting a default value (!== null) will mark this arg as optionnal else it will be a required arg
  * @param str    $desc       set the description for this argument used for the --help command
  * @param mixed  $valid_cb   You can use a callback function to check your argument at the start time
  *                           such function will receive the value given to arg by user on the command line
  *                           the callback func must return either: FALSE -> so program will display an error and exit
  *                                                                 TRUE or null -> nothing happen all is ok
  *                                                                 mixed -> the value will be replaced by the returned mixed
  * @param str    $delim      delimiter used to explode multiple value agurment
  */
  function define_arg($longname,$shortname=null,$default=null,$desc='** no description available **',
                      $valid_cb=null,$delim=null){
    $this->known_args['--'.$longname] = $longname;
    $this->_args[$longname] = array('longname'=>$longname,'desc'=>$desc);
    if(! is_null($valid_cb) ) 
      $this->_args[$longname]['validation_callback'] = $valid_cb;
    if(! is_null($delim) ) 
      $this->_args[$longname]['delim'] = $delim;
    if( is_null($default) ){
      $this->required_args[] = $longname;
    }else{
      $this->_args[$longname]['dflt'] = $default;
      $this->setted_args[$longname]   = $default;
    }
    if(! is_null($shortname)){
      $this->_args[$longname]['short'] = $shortname;
      $this->known_args['-'.$shortname]= $longname;
    }
  }
  /**
  * affiche l'aide du programme
  */
  function display_help($exitcode=0){
    $help = '';
    $max_len=0;
    $appname = $this->tagged_string(basename($_SERVER['SCRIPT_NAME']),'bold|blue');
    if(strlen($this->app_desc))
      fwrite(STDOUT,"$this->app_desc\n");
    fwrite(STDOUT,"-h,--help display this help\n");
    $i=0;
    # Display help for args
    if( count($this->known_args)){
      ksort($this->known_args);
      $rows[$i] = "\n### OPTIONS LIST FOR $appname\n";
      foreach($this->_args as $argname=>$arg){
        # $this->tagged_string(print_r($arg,1),'red',1);
        $rows[++$i][0] = ($arg['short']?'-'.$arg['short'].',':'')." --$argname";
        $rows[$i][1] = (@$arg['dflt']?"(Default value: '$arg[dflt]') ":'').$arg['desc']
                      .(@strlen($arg['delim'])?" multiple values can be separated by '$arg[delim]'":'');
        if( (! is_array($this->required_args))|| (! in_array($argname,$this->required_args)) )
          $rows[$i][0] = "[".$rows[$i][0]."]";
        $max_len     = max($max_len,strlen($rows[$i][0]));
        $parsed_arg[$arg['longname']]=TRUE;
      }
    }
    # Display help for flags
    if( isset($this->flags) && count($this->flags)){
      ksort($this->flags);
      $rows[++$i] = "\n### FLAGS / SWITCHES LIST FOR $appname\n";
      # $this->show($this);
      foreach($this->flags as $flagname=>$flag){
        $rows[++$i][0] = implode(', ',array_keys($this->known_flags,$flagname));
        $rows[$i][1]   = (isset($flag['dflt'])?"(Default: ".($flag['dflt']?'on':'off').") ":'')."switch '".$flagname."' to on. ".$flag['desc'];
        # is optional?
        if( (! is_array($this->required_flags)) || (! in_array($flagname,$this->required_flags)) )
          $rows[$i][0] = '['.$rows[$i][0].']';
        $max_len       = max($max_len,strlen($rows[$i][0]));
        # check for unflags
        if( @is_array($this->unflags) && $unflags = array_keys($this->unflags,$flagname)){
          $rows[++$i][0] = implode(', ',$unflags);
          $rows[$i][1]   = 'switch \''.$flagname.'\' to off.';
          if( (!is_array($this->required_flags)) || (! in_array($flagname,$this->required_flags)) )
            $rows[$i][0] = '['.$rows[$i][0].']';
          $max_len      = max($max_len,strlen($rows[$i][0]));
        }
      }
    }
    $max_len +=4; $split = 74;
    $blank    = str_repeat(' ',$max_len);
    $desclen  = $split-$max_len;
    foreach($rows as $row){
      if(is_string($row)){fwrite(STDOUT,$row);continue;} # echo single lines
			list($col1,$col2) = $row;
      # print first col
			fwrite(STDOUT,$col1.str_repeat(' ',max(0,strlen($blank)-strlen($col1))) );
      # print 2d col	
      while(strlen($col2) > $desclen){
				if( ($lnpos = strpos($col2,"\n")) && $lnpos < $desclen){
					fwrite(STDOUT,substr($col2,0,$lnpos)."\n$blank");
					$col2 = substr($col2,$lnpos+1);
					continue;
				}
				$last_isspace = (bool) ($col2[$desclen-1]==' ');
				$next_isspace = (bool) ($col2[$desclen]==' ');
				$next2_isspace=(bool) (isset($col2[$desclen+1])?($col2[$desclen+1]==' '):TRUE);
				if($last_isspace){
					fwrite(STDOUT,substr($col2,0,$desclen)."\n$blank");
					$col2 = substr($col2,$desclen);
				}elseif($next_isspace){
					fwrite(STDOUT,substr($col2,0,$desclen)."\n$blank");
					$col2 = substr($col2,$desclen+1);
				}elseif($next2_isspace){
					fwrite(STDOUT,substr($col2,0,$desclen+1)."\n$blank");
					$col2 = substr($col2,$desclen+2);
				}else{
					fwrite(STDOUT,substr($col2,0,$desclen)."-\n$blank");
					$col2 = substr($col2,$desclen);
				}
      }
      fwrite(STDOUT,$col2."\n");
    }
    exit($exitcode);
  }
  /** add an header string to explain the programm behaviour, will be displayed on the help screen */
  function set_app_desc($string){
    $this->app_desc = $string;
  }
  /**
  * parse command line parameters.
	* You must call this method after args and flags definition and before any console_app::get_arg() call
  */
  function parse_args(){
    $argv = $_SERVER['argv'];
    $argc = $_SERVER['argc'];
    # si pas d'argument on retourne 
    if(! $argc>1) 
      return FALSE;
    # we parse each args
    for($i=1;$i<$argc;$i++){
      $arg = $argv[$i];
      if( in_array($arg,array('--help','-h')) ) # check for help 
        return $this->display_help(0);
      
      if( substr($arg,0,1)!='-' ){ # not a flag or arg
				$this->unknown_args[] = $arg;
				continue;
      }
        
      if( isset($this->known_args[$arg]) ){ # Known argument so we process it
        $name = $this->known_args[$arg]; # get arg name
        # get his value
				if(! isset($argv[$i+1]) ) continue;
        if(! isset($this->_args[$name]['delim']) )# unique value entry
          $this->setted_args[$name] = isset($argv[++$i])?$argv[$i]:FALSE;
        else # multiple value argument 
          $this->setted_args[$name] = split($this->_args[$name]['delim'],$argv[++$i]);
        if(isset($this->_args[$name]['validation_callback'])){ # run optionnal validation callback
          $cb_ret = call_user_func($this->_args[$name]['validation_callback'],$this->setted_args[$name]);
          if($cb_ret===FALSE){ # callback failed so display error message and then help
            $this->tagged_string("** '".$arg .' '.$argv[$i]."' Invalid value given for $name **",'red|bold',1);
            return $this->display_help(-1);
          }elseif(! in_array($cb_ret,array(TRUE,NULL),TRUE) ){ # callback returned a value so we override user value with this one
            $this->setted_args[$name] = $cb_ret;     # get the args value
          }
        }
      }elseif( isset($this->known_flags[$arg]) ){     # known flag
        $name = $this->known_flags[$arg]; # get arg name
        $this->setted_flags[$name] = TRUE;
      }elseif( isset($this->unflags[$arg])){ # known unflag
        $this->setted_flags[$this->unflags[$arg]] = FALSE;
      }else{ # unknown flag or args
				$_arg = substr($arg,1);
				if(isset($this->known_flags)){
					foreach($this->known_flags as $k=>$v){
						if(! strlen($_arg) ) break;
						$k=substr($k,1);
						if( substr_count($_arg,$k) ){
							$this->setted_flags[$v] = TRUE;
							$_arg = str_replace($k,'',$_arg);
						}
					}
				}
				if(isset($this->unflags)){
					foreach($this->unflags as $k=>$v){
						if(! strlen($_arg) ) break;
						$k=substr($k,1);
						if( substr_count($_arg,$k) ){
							$this->setted_flags[$v] = FALSE;
							$_arg = str_replace($k,'',$_arg);
						}
					}
				}
				if( strlen($_arg) ){
					$this->tagged_string("** undefined parameter $arg **",'red',1);
					return $this->display_help(-1);
				}
      }
    }
    if( is_array($this->required_args))
      foreach($this->required_args as $arg){
        if(! isset($this->setted_args[$arg]))
          $this->msg_error("** Missing required $arg parameter (".($this->_args[$arg]['short']?'-'.$this->_args[$arg]['short'].', ':'')."--$arg)**",TRUE);
      }
    if( is_array($this->required_flags) )
      foreach($this->required_flags as $flag){
        if(! isset($this->setted_flags[$flag]))
          $this->msg_error("** Missing required flag: $flag ("
                          .($this->flags[$flag]['short']?'-'.$this->flags[$flag]['short'].', ':'')
                          ."--$flag) **",TRUE);
      }
  }
  /**
  * return a tagged console string, used to print color string on the command line interface.
  * @param string $string the string to display
  * @param string $tag the tag identifier to use (like color name) multiple tags can be passed separated by '|'
	* @param bool   $stdout if set to true then send string to stdout instead of return it
  */
  function tagged_string($string,$tag='blink',$stdout=FALSE){
    # define some escaped commands code
    $codes = array( # some cool stuff
                    'reset'      => 0,   'bold'       => 1,
                    'underline'  => 4,   'nounderline'=> 24,
                    'blink'      => 5,   'reverse'    => 7,
                    'normal'     => 22,  'blinkoff'   => 25,
                    'reverse'    => 7,   'reverseoff' => 27,
                    # some foreground colors
                    'black'      => 30,  'red'        => 31,
                    'green'      => 32,  'brown'      => 33,
                    'blue'       => 34,  'magenta'    => 35,
                    'cyan'       => 36,  'grey'       => 37,
                    # Some background colors
                    'bg_black'   => 40,  'bg_red'     => 41,
                    'bg_green'   => 42,  'bg_brown'   => 43,
                    'bg_blue'    => 44,  'bg_magenta' => 45,
                    'bg_cyan'    => 46,  'bg_white'   => 47,
                  );
    if(substr_count($tag,'|')){ # parse multiple tags
      $tags = explode('|',$tag);
      $str='';
      foreach($tags as $tag){
        $str[]= isset($codes[$tag])?$codes[$tag]:30;
      }
      $str = "\033[".implode(';',$str).'m'.$string."\033[0m";
    }else{
      if( in_array($codes[$tag],array(4,5,7)) ){
        $end = "\033[2".$codes[$tag].'m';
      }else{
        $end = "\033[0m";
      }
      $str = "\033[".(isset($codes[$tag])?$codes[$tag]:30).'m'.$string.$end;
    }
    if(! $stdout)
      return $str;
    fwrite(STDOUT,"$str\n");
  }
  
	function clear_screen(){
		fwrite(STDOUT,"\033[2J");
  }
  
	/**
	* helper methods to refresh a progress bar
	* @param mixed  $value     int/float current value relative to $max
	* @param string $msg       message to display if you want to replace the old one.
	* @param bool   $dontclean if set to TRUE then won't replace previous displayed bar but just print at the end of the script
	*/
	function refresh_progress_bar($value,$msg=null,$dontclean=FALSE){
		console_app::progress_bar($value,$msg,null,null,null,null,!$dontclean);
	}
	
	/**
	* set and display a progress bar
	* @param mixed  $val int/float value relative to $max
	* @param string $msg message to display with the bar (you can either pass an array(string msg,string tag))
	* @param int    $w   length of the bar (in character)
	* @param mixed  $max int/float maximum value to display
	* @param string $formatString is a string to custom the display of your progress bar
	*                             this are the replacement made in the string before displaying:
	*                             %V will be replaced by the value
	*                             %S will be replaced by the msg
	*                             %M will be replaced by the maxvalue
	*                             %B will be replaced by the bar
	*                             %P will be replaced by the percent value
	* @param array $style   permit you to custom the bar style array(done_style,todo_style)
	*                       where (todo|done)_style can be a string (character) or an array( (str) char,(str) tag as in tagged_string)
	* @param bool  $refresh set this to true if you want to erase last printed progress 
	*                       (you mustn't have any output between this call and the last one to get it work properly)
	*                       normaly you won't have to use this, use the helper methods refresh_progress_bar().
	*/
	function progress_bar($val,$msg=null,$max=100,$w=40,$formatString="%S\n\t%B %P",$style=array(array('=','bold|green'),' '),$refresh=FALSE){
		static $pgdatas;
		$args = array('msg','w','max','formatString','style');
		foreach($args as $arg){
			if( is_null($$arg) ) # set null args to previous values 
				$$arg = isset($pgdatas[$arg])?$pgdatas[$arg]:null;
			else # keep trace for nex call
				$pgdatas[$arg] = $$arg;
		}
		# clear previously displayed bar
		if($refresh && isset($pgdatas['nbline']) ){
			for($i=0;$i<$pgdatas['nbline'];$i++)
				fwrite(STDOUT,"\033[1A\033[K");	# up one line and clear it
		}
		# calc some datas
		$good = max(0,round($val*$w/$max));
		$bad  = max(0,$w-$good);
		
		# make some style
		list($done_chr,$todo_chr) = $style;
		if(is_array($done_chr))
			list($done_chr,$done_tag) = $done_chr;
		if(is_array($todo_chr))
			list($todo_chr,$todo_tag) = $todo_chr;
		$good = str_repeat($done_chr,$good);
		$bad = str_repeat($todo_chr,$bad);
		if( isset($done_tag) )
			$good = console_app::tagged_string($good,$done_tag);
		if( isset($todo_tag) )
			$bad = console_app::tagged_string($bad,$todo_tag);
		
		# then render the bar
		if( is_array($msg) ) $msg = console_app::tagged_string($msg[0],$msg[1]);
		$bar = '['.$good.$bad.']';
		$per = round($val/$max*100,1);
		$str = str_replace(array('%V','%M','%S','%B','%P'),array($val,$max,$msg,$bar,"$per%"),$formatString)."\n";
		$pgdatas['nbline'] = substr_count($str,"\n");
		fwrite(STDOUT,$str);
	}
	
	/**
	* print a 2D array as a table
	* @param array $table
	* @param array $styles list of tag for each row /cells -> array(rowid=>array(colid=>'tag','dflt'=>'tag')) where default will apply to all cell in row
	* @param bool  $return if true then return the string instead of printing it to the screen
	*/
	function print_table($table,$styles=null,$return=FALSE){
		if(! (is_array($table)  && isset($table[0]) && is_array($table[0]) ) )
			return FALSE;
		list($headers) = $table;
		$headers = array_keys($headers);
		if( is_string($headers[0]) ){
			foreach($headers as $k=>$v){
				unset($headers[$k]);
				$headers[$v] = $v;
			}
			array_unshift($table,$headers);
			if(! is_null($styles) )
				array_unshift($styles,null);
		}else{
			$headers = FALSE;
		}
		
		# calc width for each cols
		foreach($table as $rowid=>$row){
			foreach($row as $colid=>$col){
				$clines = explode("\n",$col);
				$nbline = count($clines);
				foreach($clines as $l){
					$widths[$colid] = isset($widths[$colid])?max($widths[$colid],strlen($l)):strlen($l);
				}
				$heights[$rowid] = isset($heights[$rowid])?max($heights[$rowid],$nbline):$nbline;
			}
		}
		
		$twidth = array_sum($widths) + 3*count($widths) + 1;
		$nbr=0;
		# now render
		$strOut = '';
		foreach($table as $rid=>$row){ # each  table rows
			$strOut .= str_repeat('-',$twidth)."\n"; # draw top row border
			
			# on doit ajouter les lignes manquantes à chaque cellules
			$rowlines = array();
			$rowstyle = (isset($styles[$rid])&&isset($styles[$rid]['dflt']))?$styles[$rid]['dflt']:null;
			foreach($row as $cid=>$col){
				$col = explode("\n",$col);
				$colstyle = $rowstyle.((isset($styles[$rid])&&isset($styles[$rid][$cid]))?($rowstyle?'|':'').$styles[$rid][$cid]:null);
				for($i=0;$i<$heights[$rid];$i++){
					if(! isset($col[$i]) ){
						$rowlines[$i][$cid] = str_repeat(' ',$widths[$cid]);
					}else{
						$cline = $colstyle?console_app::tagged_string($col[$i],$colstyle):$col[$i];
						$rowlines[$i][$cid] = $cline.str_repeat(' ',max(0,$widths[$cid]-strlen($col[$i])));
					}
				}
			}
			
			# print row
			for($i=0;$i<$heights[$rid];$i++){
				if( $nbr==0 && $headers)
					$strOut .= console_app::tagged_string('| '.implode(' | ',$rowlines[$i]).' |','reverse')."\n"; 
				else
					$strOut .= '| '.implode(' | ',$rowlines[$i])." |\n"; 
			}
			$nbr++;
		}
		$strOut .= str_repeat('-',$twidth)."\n"; # draw bottom table border
		if($return)
			return $strOut ;
		fwrite(STDOUT,$strOut);
	}
	
	function msg($msg,$tag=null){
    if($tag)
      console_app::tagged_string($msg,$tag,1);
    else
      fwrite(STDOUT,"$msg\n");
  }
  /**
  * display an information msg to the user (blue)
  * @param string $msg;
  */
  function msg_info($msg){
    console_app::msg('::information:: '.$msg,'blue');
    return FALSE;
  }
  /**
  * display an error message
  * @param string $msg
  * @param bool   $fatal stop the script if set to yes
  * @param int    $exitcode code to return on fatal error (-1 is default)
	* @return FALSE
	*/
  function msg_error($msg,$fatal=FALSE,$exitcode=-1){
    $msg = console_app::tagged_string('::'.($fatal?'FATAL_':'').'ERROR:: '.$msg,'red|bold',FALSE);
		fwrite(STDERR,"$msg\n");
		error_log($msg); # do this for Qaleo

		if($fatal)
      exit($exitcode);
    return FALSE;
  }
  
	/**
  * display a confirmation message (yes|no choice)
  * @param string $msg
  * @param string $tag optionnaly tagg the string as in tagged string
	* @param bool   $dfltIsYes if true then the default value is yes instead of no
	* @return bool
  */
  function msg_confirm($msg,$tag='brown',$dfltIsYes=FALSE){
		$dflt= $dfltIsYes?'yes':'no';
    $msg = "::confirmation:: $msg\nYes | No ($dflt)?";
    $res = console_app::read($tag?console_app::tagged_string($msg,$tag):$msg,$dflt);
    if(preg_match('!^[yo]([eu][si])?$!i',$res))
      return TRUE;
    else
      return FALSE;
  }
	
	/**
	* same as the read method but with the possibility to tagg the displayed message
	*/
	function msg_read($msg,$tag='blue',$dflt=null,$check_exit=TRUE){
		if(isset($this))
			return $this->read(console_app::tagged_string($msg,$tag),$dflt,$check_exit);
		else
			return console_app::read(console_app::tagged_string($msg,$tag),$dflt,$check_exit);
	}
  /**
  * lit une entree utilisateur sur STDIN
  * @param string $string message a afficher
  * @param string $dflt   valeur par defaut optionnelle,
  *               si l'utilisateur ne saisie rien alors c'est la valeur par defaut qui sera retourné.
  * @param bool   $check_exit if the user input exit on a read request the program default behaviour is to exit itelf
  *                           you can disablle this feature by setting this to FALSE.
	* return string
  */
  function read($string='Wait for input:',$dflt=null,$check_exit=TRUE){
		$_lnOnRead = isset($this)? $this->_lnOnRead : TRUE;
		$_readline = isset($this)? $this->_readline : function_exists('readline');
		# $_readline = isset($this)? $this->_readline : FALSE;
		$_captureEOT = isset($this)? $this->_captureEOT: TRUE;
		if( $_lnOnRead )
			$string .= "\n";
    if( $_readline ){
			$read = readline($string);
			if( $read === FALSE ){ # capture End Of Transmission (CTRL+D)
				if( $_captureEOT ) 
					exit();
				return FALSE;
			}
		}else{
			if($string) fwrite(STDOUT,$string);
			$read=fgets(STDIN,4096);
			if(! $read){ # capture End Of Transmission (CTRL+D)
				if($_captureEOT)
					exit();
				else
					return FALSE;
			}
			# strip newline
			$read = preg_replace('![\r\n]+$!','',$read);
		}
		
    if($check_exit && $read =='exit'){
      console_app::msg_info('execution stopped by user.');
      exit(0);
    }
		
    # check default value 
    if( (!$read) && !is_null($dflt))
			return $dflt;
		if( $_readline )
			readline_add_history($read);
    return $read;
  }
  
	/**
  * debug function, print the structure of any variable
  * @param mixed  $var 
  * @param string $tag
  * @param bool   $exit
  */
  function show($var,$tag='bold|red',$exit=FALSE){
    console_app::tagged_string(print_r($var,1),$tag,1);
    if($exit) exit;
  }
  /**
  * interactively reconfigure a simple configuration file
  * @param string $cfg_file 		the config file to reconfigure
  * @param string $cfg_template template file to use for the config file
  * @param array  $dflt_vals    you can pass an array of default value for the user prompt
  *                             ie: array('CONFIG_PARAM'=>'PARAM_VALUE'[,...]);
  * @param bool   $exitonerror  will stop the programm on error if set to true
  * @return bool
  * @require write_conf_file(),parse_conf_file() 
  */
  function interactive_reconfigure($cfg_file,$cfg_template=null,$dflt_vals=null,$exitonerror=FALSE,$returnconf=FALSE){
    require_once(dirname(__file__).'/fx-conf.php');
		console_app::msg("** Interactive Reconfigure -> $cfg_file **",'blue');
    
		# prepare the template config array
		if(is_null($cfg_template))
				$cfg_template = $cfg_file;
		if(! is_file($cfg_template)){
			console_app::msg_error("Invalid configuration file will try: $cfg_file");
			if(! is_file($cfg_template = $cfg_file) ){
				if(! is_array($dflt_vals) )
					return console_app::msg_error("Can't reconfigure without any valid configuration template!",$exitonerror);
				else
					$tpl_cfg = $dflt_vals;
			}
		}
		
		# parse template config
		if( (! @is_array($tpl_cfg)) && ! is_array($tpl_cfg = parse_conf_file($cfg_template,TRUE)) )
			return console_app::msg_error("Empty configuration file. Don't know what to configure",$exitonerror);
		
    # user reconfigure
    foreach($tpl_cfg as $k=>$v){
      if(isset($dflt_vals[$k]))
        $v = $dflt_vals[$k];
      $tpl_cfg[$k] = console_app::read("set '$k' ($v): ",$v);
    }
    if(! write_conf_file($cfg_file,$tpl_cfg,TRUE) )
      return console_app::msg_error("Can't write file : $cfg_file",$exitonerror);
    fwrite(STDOUT,"configuration DONE.\n".str_repeat('-',70)."\n");
    if($returnconf)
      return $tpl_cfg;
    return TRUE;
  }
}

?>
Return current item: Console App