<?php
/**
* Copyright (c) 2015, Till Wehowski
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of frdl/webfan nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY frdl/webfan ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL frdl/webfan BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Till Wehowski <software@frdl.de>
* @package webfan://frdl.aSQL.Engines.Terminal.CLI.code
* @uri /v1/public/software/class/webfan/frdl.aSQL.Engines.Terminal.CLI/source.php
* @version 1.0.0.2
* @file frdl\aSQL\Engines\Terminal\CLI.code.php
* @role Command Line Parser
* @copyright 2015 Copyright (c) Till Wehowski
* @license http://look-up.webfan.de/bsd-license bsd-License 1.3.6.1.4.1.37553.8.1.8.4.9
* @link http://interface.api.webfan.de/v1/public/software/class/frdl/frdl.aSQL.Engines.Terminal.CLI/doc.html
* @OID 1.3.6.1.4.1.37553.8.1.8.8 webfan-software
* @requires PHP_VERSION 5.3 >=
* @requires webfan://frdl.webfan.Autoloading.SourceLoader.code
* @api http://api.webfan.de
* @reference http://www.webfan.de/install/
*
*/
namespace frdl\aSQL\Engines\Terminal;
abstract class CLI
{
const MODE_CLI = 1;
const MODE_HTTP = 2;
const REGEX_CML = <<<REGEX
/
(?:
" ((?:(?<=\\\\)"|[^"])*) "
|
' ((?:(?<=\\\\)'|[^'])*) '
|
(\S+)
)
/x
REGEX;
const REGEX_DELIM = '/^;|\|$/';
const DELIM = ';';
const SPACE = ' ';
protected $state;
protected $mode;
protected $shell = array(
'name' => 'frdl',
'prompt' => '!#frdl:>',
'commands' => array(),
'options' => array(),
'flags' => array(),
'arguments' => array(),
'operators' => array(),
);
protected $IN;
protected $tokens;
protected $batch;
/**
* processing
*/
function __construct(){
$this->IN = '';
$this->batch = array();
$this->mode = (PHP_SAPI === 'cli') ? self::MODE_CLI : self::MODE_HTTP;
$this->boot();
}
/**
* To inherit...
*/
/**
* Run CLI
*/
abstract protected function boot();
abstract protected function _exec($args);
abstract protected function force_state($state);
abstract public function parseQuery();
abstract public function validateQuery();
/**
* Build CLI
*/
abstract public function add_command($command, callable $callable);
abstract public function add_option(mixed $settings);
abstract public function add_flag(mixed $settings);
abstract public function add_argument(mixed $settings);
/**
* Core
*/
public function state($state = null){
if($state !== null)$this->force_state($state);
return $this->state;
}
public function normalize($cml){
// return preg_replace("/[\s+]/", " ", trim($cml));
return $cml;
}
public function unparse_args($args){
$tokens = array();
$tokens[$args['command']['pos']] = $args['command']['cmd'];
foreach($args['options'] as $k => $opt){
$tokens[$opt['pos']] = '--'.$opt['opt'].'='.$opt['quotes'].$opt['value'].$opt['quotes'];
// $tokens[$opt['pos']] = '--'.$opt['opt'].'="'.$opt['value'].'"';
}
foreach($args['flags'] as $k => $f){
if(!isset($tokens[$f['pos']]))$tokens[$f['pos']] = '-';
$tokens[$f['pos']] .= $f['flag'];
}
foreach($args['arguments'] as $k => $c){
$tokens[$c['pos']] = $c['cmd'];
}
ksort($tokens);
return implode(self::SPACE, $tokens);
}
public function unparse($batch){
$s = array();
foreach($batch as $pos => $args){
$s[] = $this->unparse_args($args);
}
return implode(self::DELIM."\r\n", $s);
}
public function parse($cml = null){
$cml = (null === $cml) ? $this->IN : $cml;
$cml = rtrim($cml, ';|'.self::SPACE);
$cml .= ';';
$batch = array();
$p = 0;
$batch[$p] = array();
preg_match_all(self::REGEX_CML, $cml, $matches, PREG_SET_ORDER);
$args = array();
foreach ($matches as $pos => $match) {
if (isset($match[3])) {
$a = $match[3];
} elseif (isset($match[2])) {
$a = str_replace(array('\\\'', '\\\\'), array("'", '\\'), $match[2]);
} else {
$a = str_replace(array('\\"', '\\\\'), array('"', '\\'), $match[1]);
}
if(preg_match(self::REGEX_DELIM, $a)){
$batch[$p] = $this->arguments( $args );
$args = array();
$p++;
}elseif(substr($a,-1)===';' || substr($a,-1)==='|'){
$a = rtrim($a, ';|');
$args[] = $a;
$batch[$p] = $this->arguments( $args );
$args = array();
$p++;
}else{
$args[] = $a;
}
}
$this->batch = $batch;
return $batch;
}
public function exe($cml = ''){
global $argv;
$this->IN = ($this->mode === self::MODE_CLI) ? implode(self::DELIM, $argv) : $cml;
$this->parse();
$this->_exec($this->args);
return $this;
}
protected function arguments( $args = null )
{
if(!is_array($args) && is_array($this->args))$args = $this->args;
if($this->mode === self::MODE_CLI)array_shift( $args );
$escape = false;
$ret = array
(
'command' => array(),
'commands' => array(),
'options' => array(),
'flags' => array(),
'arguments' => array(),
);
$k = -1;
while( $arg = array_shift($args) )
{
$k++;
if ( substr( $arg, 0, 2 ) === '--' )
{
$value = "";
$com = substr( $arg, 2 );
if(strpos($com,'='))
{
list($com,$value) = explode("=",$com,2);
}
$quotes = '';
if( (substr($value, 0, 1) === '"' || substr($value, 0, 1) === "'") && substr($value, strlen($value)-1, 1) !== "'" && substr($value, strlen($value)-1, 1) !== '"' )
{
$quotes = substr($value, 0, 1);
while(count($args) > 0)
{
$v = array_shift($args);
if(strpos($v,'\\') !== FALSE)$escape = true;
$value .= $v.self::SPACE;
if($escape = false && ((strpos($v,'"') !== FALSE && $quotes === '"') || (strpos($v,"'") !== FALSE && $quotes === "'")))break;
if($escape = true && ((strpos($v,'"') !== FALSE && $quotes === '"') || (strpos($v,"'") !== FALSE && $quotes === "'")))$escape = false;
}
$value = trim($value, self::SPACE);
$value = trim($value, '"');
$value = trim($value, "'");
}
$value = trim($value , '"\'');
if(empty($value))$value = true;
$ret['options'][] = array(
'opt' => $com,
'value' => $value,
'pos' => $k,
'quotes'=>$quotes,
);
continue;
}
if ( substr( $arg, 0, 1 ) === '-' && substr( $arg, 0, 2 ) != '--')
{
for ($i = 1; isset($arg[$i]) ; $i++)
$ret['flags'][$arg[$i]] = array(
'flag' => $arg[$i],
'pos' => $k,
);
continue;
}
$ret['commands'][] =array( 'cmd' => $arg, 'pos' => $k,);
continue;
}
$ret['arguments'] = $ret['commands'];
$ret['command'] = array_shift($ret['arguments']);
unset($ret['commands']);
return $ret;
}
}
|