git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@6096 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
448 lines
14 KiB
PHP
448 lines
14 KiB
PHP
<?php
|
|
/** DomFramework
|
|
* @package domframework
|
|
* @author Dominique Fournier <dominique@fournier38.fr>
|
|
* @license BSD
|
|
*/
|
|
|
|
/** Allow to interract with controllers and models from the CLI */
|
|
class cli
|
|
{
|
|
/** Run in CLI mode with parameters
|
|
* Example of cli code :
|
|
* #!/usr/bin/php5
|
|
* <?php
|
|
* require ("domframework/cli.php");
|
|
* $cli = new cli;
|
|
* $cli->run();
|
|
*/
|
|
/** The expert mode allow to see/execute the models */
|
|
private $EXPERT = false;
|
|
|
|
/** Quiet mode don't display empty results */
|
|
private $QUIET;
|
|
|
|
/** Store the controllers pathes to test
|
|
*/
|
|
private $controllersPath = array ("controllers/*.php");
|
|
|
|
/** Store the models pathes to test
|
|
*/
|
|
private $modelsPath = array ("models/*.php");
|
|
|
|
/** The construtor define the catching of the errors */
|
|
public function __construct ()
|
|
{
|
|
global $argv;
|
|
$launcher = $argv[0];
|
|
chdir (dirname ($argv[0])."/..");
|
|
// Catch the trigger_errors and display them politely
|
|
set_error_handler(array(&$this, "cliErrorHandler"));
|
|
}
|
|
|
|
/** Define the controllers path
|
|
* @param string|array $path The controllers pathes to use
|
|
*/
|
|
public function controllersDir ($path)
|
|
{
|
|
if (! is_array ($path))
|
|
$path = array ($path);
|
|
$this->controllersPath = $path;
|
|
}
|
|
|
|
/** Define the models path
|
|
* @param string|array $path The models pathes to use
|
|
*/
|
|
public function modelsDir ($path)
|
|
{
|
|
if (! is_array ($path))
|
|
$path = array ($path);
|
|
$this->modelsPath = $path;
|
|
}
|
|
|
|
/** The error handler for CLI : display error in STDERR
|
|
* @param integer $errno The error type
|
|
* @param string $errstr The string to send in error
|
|
* @param string $errfile The error file which generate the error
|
|
* @param integer $errline The line with an error
|
|
*/
|
|
public function cliErrorHandler ($errno, $errstr, $errfile, $errline)
|
|
{
|
|
if (!(error_reporting() & $errno))
|
|
{
|
|
// This error code is not included in error_reporting
|
|
return;
|
|
}
|
|
switch($errno)
|
|
{
|
|
case E_ERROR: $severity = "ERROR"; break;
|
|
case E_WARNING: $severity = "WARNING"; break;
|
|
case E_PARSE: $severity = "PARSE"; break;
|
|
case E_NOTICE: $severity = "NOTICE"; break;
|
|
case E_CORE_ERROR: $severity = "CORE_ERROR"; break;
|
|
case E_CORE_WARNING: $severity = "CORE_WARNING"; break;
|
|
case E_COMPILE_ERROR: $severity = "COMPILE_ERROR"; break;
|
|
case E_COMPILE_WARNING: $severity = "COMPILE_WARNING"; break;
|
|
case E_USER_ERROR: $severity = "USER_ERROR"; break;
|
|
case E_USER_WARNING: $severity = "USER_WARNING"; break;
|
|
case E_USER_NOTICE: $severity = "USER_NOTICE"; break;
|
|
case E_STRICT: $severity = "STRICT"; break;
|
|
case E_RECOVERABLE_ERROR: $severity = "RECOVERABLE_ERROR"; break;
|
|
case E_DEPRECATED: $severity = "DEPRECATED"; break;
|
|
case E_USER_DEPRECATED: $severity = "USER_DEPRECATED"; break;
|
|
}
|
|
file_put_contents ("php://stderr", sprintf ("%-17s", $severity)." ".
|
|
"$errstr [".basename ($errfile) .
|
|
":$errline]\n");
|
|
if ($this->EXPERT)
|
|
{
|
|
$e = new Exception;
|
|
file_put_contents ("php://stderr",
|
|
print_r ($e->getTraceAsString(), TRUE)."\n");
|
|
}
|
|
}
|
|
|
|
/** The real application launcher and helpers
|
|
*/
|
|
public function run ()
|
|
{
|
|
global $argv;
|
|
$launcher = $argv[0];
|
|
array_shift ($argv);
|
|
|
|
if (isset ($argv[0]) && $argv[0] === "-h")
|
|
{
|
|
echo "Execute in CLI a controller or a model (in expert mode)\n";
|
|
echo " $launcher -h : display this help\n";
|
|
echo " $launcher -list : display controllers\n";
|
|
echo " $launcher -expert -list : display controllers and models\n";
|
|
echo " $launcher -listmethods <class> : \n";
|
|
echo " display the methods available in the controller class\n";
|
|
echo " $launcher -expert -listmethods <class> :\n";
|
|
echo " display the methods available in the model or controller class"
|
|
."\n";
|
|
echo " $launcher -listmethodsdetails <class> : \n";
|
|
echo " display the methods available in the controller class\n";
|
|
echo " $launcher -expert -listmethodsdetails <class> :\n";
|
|
echo " display the methods available in the model or controller class"
|
|
."\n";
|
|
echo " $launcher <class> <method> [args]\n";
|
|
echo " execute the method with the provided args\n";
|
|
echo " $launcher -expert <class> <method> [args]\n";
|
|
echo " execute the method with the provided args\n";
|
|
echo "You can replace ONE arg by a dash (-) to read from stdin\n";
|
|
echo "Arrays must be coded like key1=val1&key2=val2&key3=val3...\n";
|
|
exit;
|
|
}
|
|
|
|
$this->QUIET = FALSE;
|
|
if (isset ($argv[0]) && $argv[0] === "-q")
|
|
{
|
|
$this->QUIET = true;
|
|
array_shift ($argv);
|
|
}
|
|
|
|
$this->EXPERT = FALSE;
|
|
if (isset ($argv[0]) && $argv[0] === "-expert")
|
|
{
|
|
$this->EXPERT = TRUE;
|
|
array_shift ($argv);
|
|
}
|
|
|
|
// List the controllers and the models if the expert mode is activated
|
|
$files = array ();
|
|
foreach ($this->controllersPath as $path)
|
|
{
|
|
$files = array_merge ($files, glob ($path));
|
|
}
|
|
if ($this->EXPERT)
|
|
{
|
|
foreach ($this->modelsPath as $path)
|
|
{
|
|
$files = array_merge ($files, glob ($path));
|
|
}
|
|
}
|
|
if (count ($files) === 0 && $this->EXPERT === FALSE)
|
|
{
|
|
if (isset ($argv[0]) && $argv[0] === "-listonly")
|
|
exit;
|
|
die ("No controllers available in ".getcwd()."\n");
|
|
}
|
|
if (count ($files) === 0 && $this->EXPERT === TRUE)
|
|
{
|
|
if (isset ($argv[0]) && $argv[0] === "-listonly")
|
|
exit;
|
|
die ("No controllers/models available in ".getcwd()."\n");
|
|
}
|
|
foreach ($files as $file)
|
|
{
|
|
if (strpos ($file, "_"))
|
|
$classes[substr (strstr ($file, "_"), 1, -4)] = $file;
|
|
else
|
|
$classes[str_replace ("/", "\\", substr ($file, 0, -4))] = $file;
|
|
}
|
|
// Check if there is some classes which can be shorted (classes only in
|
|
// controllers or only in models
|
|
$shortClasses = array ();
|
|
foreach ($classes as $class=>$file)
|
|
{
|
|
$short = substr (strstr ($class, "\\"), 1);
|
|
if ($short === false) $short = $class;
|
|
if (array_key_exists ($short, $shortClasses))
|
|
unset ($shortClasses[$short]);
|
|
else
|
|
$shortClasses[$short] = $class;
|
|
}
|
|
|
|
if (isset ($argv[0]) && $argv[0] === "-listonly")
|
|
{
|
|
// Lists the classes available in controllers and models if expert mode
|
|
// Don't do any presentation to be parseable (by bash completion by
|
|
// example)
|
|
$classes = array_merge ($classes, $shortClasses);
|
|
echo implode ("\n", array_keys ($classes))."\n";
|
|
exit;
|
|
}
|
|
|
|
if (isset ($argv[0]) && $argv[0] === "-list")
|
|
{
|
|
// Lists the classes available in controllers and models if expert mode
|
|
$classes = array_merge ($classes, $shortClasses);
|
|
foreach ($classes as $key=>$class)
|
|
$classes[$key] = str_replace ("\\", "\\\\", $class);
|
|
echo "List of classes available :\n";
|
|
echo " ".implode ("\n ", array_keys ($classes))."\n";
|
|
echo "Usage : ".$launcher;
|
|
if ($this->EXPERT) echo " -expert";
|
|
echo " -listmethods <class>\n";
|
|
echo "Usage : ".$launcher;
|
|
if ($this->EXPERT) echo " -expert";
|
|
echo " -listmethodsdetails <class>\n";
|
|
exit;
|
|
}
|
|
|
|
if (isset ($argv[0]) && $argv[0] === "-listmethods" ||
|
|
isset ($argv[0]) && $argv[0] === "-listmethodsdetails" ||
|
|
isset ($argv[0]) && $argv[0] === "-listmethodsonly")
|
|
{
|
|
// Lists the methods of a class
|
|
if (!isset ($argv[1]))
|
|
die ("Missing parameter 'class'\n");
|
|
$class = $argv[1];
|
|
if (! array_key_exists ($class, $classes) &&
|
|
! array_key_exists ($class, $shortClasses))
|
|
die ("Unknown class '$class'\n");
|
|
if (array_key_exists ($class, $shortClasses))
|
|
$class = $shortClasses[$class];
|
|
$file = $classes[$class];
|
|
require_once ($file);
|
|
$refclass = new ReflectionClass ($class);
|
|
// Look at constructor parameters
|
|
// PARAMETERS MUST NOT BE OPTIONNAL
|
|
$constParams = "";
|
|
try
|
|
{
|
|
$r = new ReflectionMethod ($class, "__construct");
|
|
$params = $r->getParameters();
|
|
foreach ($params as $param)
|
|
{
|
|
$constParams .= " ";
|
|
$constParams .= " <";
|
|
$constParams .= $param->name;
|
|
$constParams .= ">";
|
|
}
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
// No constructor
|
|
}
|
|
|
|
// Look at methods
|
|
$methods = $refclass->getMethods();
|
|
if ($argv[0] !== "-listmethodsonly")
|
|
echo "List of methods available in the class '$class' :\n";
|
|
foreach ($methods as $key=>$method)
|
|
{
|
|
if ($method->name === "__construct")
|
|
continue;
|
|
// The private/protected methods can not be called
|
|
if ($method->isPrivate () || $method->isProtected ())
|
|
continue;
|
|
if ($key > 0 && $argv[0] !== "-listmethodsonly")
|
|
echo "\n";
|
|
if ($argv[0] !== "-listmethodsonly")
|
|
echo " ";
|
|
echo $method->name.$constParams;
|
|
if ($argv[0] !== "-listmethodsonly")
|
|
{
|
|
$r = new ReflectionMethod ($class, $method->name);
|
|
$params = $r->getParameters();
|
|
foreach ($params as $param)
|
|
{
|
|
echo " ";
|
|
if ($param->isOptional())
|
|
echo "[";
|
|
else
|
|
echo " <";
|
|
echo $param->name;
|
|
if ($param->isOptional())
|
|
echo "]";
|
|
else
|
|
echo ">";
|
|
}
|
|
|
|
if ($argv[0] === "-listmethodsdetails")
|
|
{
|
|
echo "\n";
|
|
echo trim (substr ($method->getDocComment(), 3, -2));
|
|
}
|
|
}
|
|
echo "\n";
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// Execution of the class->method
|
|
if (!isset ($argv[0]))
|
|
{
|
|
echo "No class to execute provided\n";
|
|
echo "Usage : $launcher -list\n";
|
|
echo "Usage : $launcher -expert -list\n";
|
|
exit;
|
|
}
|
|
$class = $argv[0];
|
|
if (! array_key_exists ($class, $classes) &&
|
|
! array_key_exists ($class, $shortClasses))
|
|
die ("Unknown class '$class'\n");
|
|
if (array_key_exists ($class, $shortClasses))
|
|
$class = $shortClasses[$class];
|
|
$file = $classes[$class];
|
|
require_once ($file);
|
|
array_shift ($argv);
|
|
if (!isset ($argv[0]))
|
|
die ("No method for class '$class' to execute\n");
|
|
$method = $argv[0];
|
|
array_shift ($argv);
|
|
$refclass = new ReflectionClass ($class);
|
|
$methods = $refclass->getMethods();
|
|
$found = FALSE;
|
|
foreach ($methods as $meth)
|
|
{
|
|
if ($meth->name === $method)
|
|
{
|
|
$found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($found === FALSE)
|
|
die ("Method '$method' not available in class '$class'\n");
|
|
$min = 0; $max = 0;
|
|
$paramsConst = array ();
|
|
try
|
|
{
|
|
$r1 = new ReflectionMethod ($class, "__construct");
|
|
$paramsConst = $r1->getParameters();
|
|
$min = $max = count ($paramsConst);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
// No constructor available in class
|
|
}
|
|
|
|
$r2 = new ReflectionMethod ($class, $method);
|
|
$params = $r2->getParameters();
|
|
foreach ($params as $param)
|
|
{
|
|
if (!$param->isOptional())
|
|
$min++;
|
|
$max++;
|
|
}
|
|
|
|
if (in_array ("-?", $argv))
|
|
{
|
|
// Question mark display the PHPDoc of the method
|
|
$comment = trim (substr ($r1->getDocComment (), 3, -2));
|
|
if ($comment !== "") $comment .= "\n";
|
|
$comment .= trim (substr ($r2->getDocComment (), 3, -2));
|
|
$comment = preg_replace ("#^\s*\*\s*#m", "", $comment);
|
|
$comment = preg_replace ("#@param .+ \\$(\w+) #U", "<\\1> ", $comment);
|
|
$params = "";
|
|
foreach ($r1->getParameters() as $param)
|
|
$params .= "<".$param->name."> ";
|
|
foreach ($r2->getParameters() as $param)
|
|
$params .= "<".$param->name."> ";
|
|
echo "Parameters : $params\n$comment\n";
|
|
exit;
|
|
}
|
|
unset ($r1);
|
|
unset ($r2);
|
|
|
|
if (count ($argv) < $min)
|
|
die ("Not enough parameters provided to method\n");
|
|
if (in_array ("-", $argv))
|
|
{
|
|
$tab = array_keys ($argv, "-");
|
|
$keyStdIn = reset ($tab);
|
|
$dataStdIn = file_get_contents ("php://stdin");
|
|
$argv[$keyStdIn] = $dataStdIn;
|
|
}
|
|
|
|
// Convert "toto=ror&ypyp=oo" arg to array("toto"=>"ror","ypyp"=>"oo")
|
|
// (Array management in CLI)
|
|
foreach ($argv as $key=>$arg)
|
|
{
|
|
// Don't modify the stdin
|
|
if (isset ($keyStdIn) && $key === $keyStdIn)
|
|
continue;
|
|
$pairs = explode('&', $arg);
|
|
|
|
foreach($pairs as $pair)
|
|
{
|
|
@list ($name, $value) = explode ('=', $pair, 2);
|
|
if ($value === null)
|
|
{
|
|
$argv[$key] = $name;
|
|
}
|
|
else
|
|
{
|
|
if (! is_array ($argv[$key]))
|
|
$argv[$key] = array ();
|
|
$argv[$key][$name] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Manage a parameter in the constructor of the class
|
|
$paramConst = array ();
|
|
if (count ($paramsConst) > 0)
|
|
{
|
|
$paramConst = array_slice ($argv, 0, count ($paramsConst));
|
|
$argv = array_slice ($argv, count ($paramsConst));
|
|
}
|
|
|
|
try
|
|
{
|
|
$classReflection = new ReflectionClass($class);
|
|
$r = $classReflection->newInstanceArgs($paramConst);
|
|
$s = call_user_func_array(array($r, $method), $argv);
|
|
if ($this->QUIET === false || $s !== "" && $s !== array () && $s !== null)
|
|
{
|
|
if (is_string ($s))
|
|
echo $s;
|
|
elseif (is_array ($s))
|
|
print_r ($s);
|
|
else
|
|
var_dump ($s);
|
|
}
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
file_put_contents("php://stderr", $e->getMessage()."\n");
|
|
if ($this->EXPERT)
|
|
file_put_contents ("php://stderr", $e->getTraceAsString()."\n");
|
|
}
|
|
exit;
|
|
}
|
|
}
|