*/ /** 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 * run(); */ /** The expert mode allow to see/execute the models */ private $EXPERT = false; /** Quiet mode don't display empty results */ private $QUIET; /** 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")); } /** 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 : \n"; echo " display the methods available in the controller class\n"; echo " $launcher -expert -listmethods :\n"; echo " display the methods available in the model or controller class" ."\n"; echo " $launcher -listmethodsdetails : \n"; echo " display the methods available in the controller class\n"; echo " $launcher -expert -listmethodsdetails :\n"; echo " display the methods available in the model or controller class" ."\n"; echo " $launcher [args]\n"; echo " execute the method with the provided args\n"; echo " $launcher -expert [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 = glob ("controllers/*.php"); if ($this->EXPERT) $files = array_merge ($files, glob ("models/*.php")); 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 \n"; echo "Usage : ".$launcher; if ($this->EXPERT) echo " -expert"; echo " -listmethodsdetails \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; } }