дык вот шарю код который немного может помочь. Если есть вопросы о том как он работает- милость прошу. Для работы нужно сделать пару ходов(забыл сказал работает на никс с ncurses)
passthru("stty sane");
passthru("stty -icanon");
… и унлокнуть инпут стрим
<?php
/*
* @author AlexanderC
*/
namespace Phverbose\CLI;
use Phverbose\Application;
class KeyBinder {
/**
* Keys map
*
* @var array
*/
private $keyMap;
/**
* Backspace char
*
* @var string
*/
private $bckspace;
/**
* The keys from the key map
*
* @var array
*/
private $mapKeys;
/**
* Array of commands
*
* @var array
*/
private $commandsDump = array();
/**
* Main costructor
*/
function __construct(){
$this->bckspace = chr(8);
// set keys map
$this->keyMap = array(
chr(27).chr(91).chr(65) => 'up',
chr(27).chr(91).chr(66) => 'down',
chr(27).chr(91).chr(68) => 'left',
chr(27).chr(91).chr(67) => 'right',
chr(127) => 'bckspace',
chr(27).chr(91).chr(51).chr(126) => 'del',
);
// set keys only
$this->mapKeys = array_keys($this->keyMap);
}
/**
* Log the previous command
*
* @param string $command
* @return void
*/
public function logCommand($command){
array_unshift($this->commandsDump, rtrim((string) $command));
reset($this->commandsDump);
}
/**
* Check if any from binded keys pressed
*
* @param string reference $input
* @return void
*/
public function bindKeys(&$input){
// check for each key
foreach($this->keyMap as $key => $prefix){
// case any action for an combination available
if(false !== strpos($input, $key)){
// collect garbages
$input = str_replace($key, '', $input);
// fire the action
$action = "{$prefix}Action";
$this->$action($input, $key);
}
}
}
/**
* Hook to remove printed characters
*
* @param int $num
* @param string $input
* @param bool $delin
* @return void
*/
public function removePrintedChars($num, &$input, $delin = false){
$num = (int) $num;
// backspace first
print str_repeat($this->bckspace, $num);
// than print whitespaces
print str_repeat(' ', $num);
// than return
print str_repeat($this->bckspace, $num);
// remove from $input
if(!$delin) return;
if(($len = strlen($input)) > 1){
$input = substr($input, 0, $len - $num);
} else {
$input = '';
}
}
/**
* The action for up key press
*
* @param string $input
* @param string $key
* @return void
*/
private function upAction(&$input, $key){
// remove: ^[[A
$this->removePrintedChars(4, $input);
if(!empty($this->commandsDump)){
$this->removePrintedChars(strlen($input), $input, true);
$current = current($this->commandsDump);
$input = $current;
print $current;
if(next($this->commandsDump) === false){
reset($this->commandsDump);
}
}
}
/**
* Down key action
*
* @param type $input
* @param type $key
* @return void
*/
private function downAction(&$input, $key){
// remove: ^[[B
$this->removePrintedChars(4, $input);
if(false !== ($prev = prev($this->commandsDump))){
$this->removePrintedChars(strlen($input), $input, true);
$input = $prev;
print $prev;
} elseif(isset($this->commandsDump[0])){
$this->removePrintedChars(strlen($input), $input, true);
$next = end($this->commandsDump);
$input = $next;
print $next;
}
}
/**
* Get available keys bindings
*
* @return array
*/
public function getKeys(){
return $this->mapKeys;
}
/**
* The action for backspace
*
* @param string $input
* @param string $key
* @return void
*/
private function bckspaceAction(&$input, $key){
// remove: ^? or ^@
$this->removePrintedChars(2, $input);
// print backspace case possible
if(($len = strlen($input)) > 0){
$this->removePrintedChars(1, $input, true);
}
}
/**
* Delete key action
*
* @param string $input
* @param string $key
* @return void
*/
private function delAction(&$input, $key){
// remove: ^[[3~
$this->removePrintedChars(5, $input);
$this->removePrintedChars(strlen($input), $input, true);
}
/**
* Action for left arrow press
*
* @param string $input
* @param string $key
*/
private function leftAction(&$input, $key){
// remove: ^[[D
$this->removePrintedChars(4, $input);
// move back case possible
if(($len = strlen($input)) > 0){
if($len == 1){
$input = "";
} else {
$input = substr($input, 0, $len - 1);
}
print $key;
}
}
/**
* Action for right arrow press
*
* @param string $input
* @param string $key
*/
private function rightAction(&$input, $key){
// remove: ^[[C
$this->removePrintedChars(4, $input);
$input .= " ";
print $key;
}
}
// AND
<?php
/*
* @author AlexanderC
*/
namespace Phverbose\CLI;
use Phverbose\Application;
class In {
use \Phverbose\Singleton;
/**
* Input dump
*
* @var string
*/
private $in;
/**
* Input buffer
*
* @var string
*/
private $buffer = '';
/**
* Key binder
*
* @var Phverbose\CLI\KeyBinder
*/
private static $keyBinder;
/**
* method use after construct
*
* @return void
*/
private function onAfterConstruct(){
self::$keyBinder = new KeyBinder;
}
/**
* Write data to the input wrapper
*
* @param string $input
* @return void
*/
public function write($input){
$this->in = rtrim($input, " \n");
}
/**
* Read data from input stream
*
* @return void
*/
public function readIn(){
do {
$input = fread(STDIN, 1);
$this->buffer .= $input;
self::$keyBinder->bindKeys($this->buffer);
Application::getInstance()->fireEvent('sys.inseq', $input);
} while($input !== "\n");
$this->write($this->buffer);
// log the command
if($this->is()){
self::$keyBinder->logCommand($this->in);
} else {
Application::getInstance()->printMe();
}
$this->buffer = '';
}
/**
* Get input buffer
*
* @return string
*/
public function read(){
return $this->in;
}
/**
* Check buffer isn't empty
*
* @return string
*/
public function is(){
return !empty($this->in);
}
/**
* Display buffer on object printing
*
* @return string
*/
public function __toString() {
return $this->in;
}
}
[/php]