eesyphp/src/Cli.php

276 lines
7.7 KiB
PHP

<?php
namespace EesyPHP;
use Exception;
class Cli {
/**
* EesyPHP core mode
*/
private static $core_mode = false;
/**
* Initialize
* @return void
*/
public static function init() {
Hook :: register('cli_set_core_mode', array('\\EesyPHP\\Cli', 'on_cli_set_core_mode'));
}
/**
* Get/set core mode
* @return bool
*/
public static function core_mode($enable=null) {
if (!is_null($enable)) {
self :: $core_mode = boolval($enable);
Hook :: trigger('cli_set_core_mode', array('enabled' => self :: $core_mode));
}
return self :: $core_mode;
}
/**
* On CLI set core mode hook
* @param \EesyPHP\HookEvent $event
* @return void
*/
public static function on_cli_set_core_mode($event) {
if ($event->enabled) {
self :: add_command(
'new_project',
array('\\EesyPHP\\Cli', 'cli_new_project'),
I18n :: ___("Create a new project using EesyPHP framework"),
null,
I18n :: ___(
"This command could be used to easily build the structure of a new project using the ".
"EesyPHP framework.")
);
}
}
/**
* Registered commands
* @var array<string,array>
*/
protected static $commands = array();
/**
* Current CLI command
* @var string|null
*/
protected static $command = null;
/**
* Add CLI command
* @param string $command The command name
* @param callable $handler The command handler
* @param string $short_desc Short command description
* @param string|null $usage_args Argument usage short message
* @param string|array<string>|null $long_desc Long command description
* @return bool
*/
public static function add_command($command, $handler, $short_desc, $usage_args=null, $long_desc=null,
$override=false) {
if (array_key_exists($command, self :: $commands) && !$override) {
Log :: error(I18n::_("The CLI command '%s' already exists."), $command);
return False;
}
if (!is_callable($handler)) {
Log :: error(I18n::_("The CLI command '%s' handler is not callable !"), $command);
return False;
}
self :: $commands[$command] = array (
'handler' => $handler,
'short_desc' => $short_desc,
'usage_args' => $usage_args,
'long_desc' => $long_desc,
);
return True;
}
/**
* Show usage message
* @param string|false $error Error message to show (optional)
* @param array $extra_args Extra arguments to use to compute error message using sprintf
* @return void
*/
public static function usage($error=false, ...$extra_args) {
global $argv;
// If extra arguments passed, format error message using sprintf
if ($extra_args) {
$error = call_user_func_array(
'sprintf',
array_merge(array($error), $extra_args)
);
}
if ($error)
echo "$error\n\n";
echo(I18n::_("Usage: %s [-h] [-qd] command\n", basename($argv[0])));
echo I18n::_(" -h Show this message\n");
echo I18n::_(" -q / -d Quiet/Debug mode\n");
echo I18n::_(" --trace Trace mode (the most verbose)\n");
echo I18n::_(" command Command to run\n");
echo "\n";
echo I18n::_("Available commands:\n");
foreach (self :: $commands as $command => $info) {
if (self :: $command && $command != self :: $command)
continue;
echo (
" ".str_replace(
"\n", "\n ",
wordwrap("$command : ".I18n::_($info['short_desc'])))
."\n\n");
echo (
" ".basename($argv[0])." $command ".
($info['usage_args']?I18n::_($info['usage_args']):'').
"\n");
if ($info['long_desc']) {
if (is_array($info['long_desc'])) {
$lines = array();
foreach ($info['long_desc'] as $line)
$lines[] = I18n::_($line);
$info['long_desc'] = implode("\n", $lines);
}
else
$info['long_desc'] = I18n::_($info['long_desc']);
echo "\n ".str_replace("\n", "\n ", wordwrap($info['long_desc']))."\n";
}
echo "\n";
}
exit(($error?1:0));
}
/**
* Handle command line arguments
* @param array|null $args Command line argurment to handle (optional, default: $argv)
* @param bool|null $core_mode Force enable/disable EesyPHP core mode (optional, default: null)
* @return void
*/
public static function handle_args($args=null, $core_mode=null) {
global $argv;
self :: core_mode($core_mode);
$args = is_array($args)?$args:array_slice($argv, 1);
$log_level_set = false;
self :: $command = null;
$command_args = array();
for ($i=0; $i < count($args); $i++) {
if (array_key_exists($args[$i], self :: $commands)) {
if (!self :: $command)
self :: $command = $args[$i];
else
self :: usage(I18n::_("Only one command could be executed !"));
}
else {
switch($args[$i]) {
case '-h':
case '--help':
self :: usage();
break;
case '-d':
case '--debug':
Log :: set_level('DEBUG');
$log_level_set = true;
break;
case '-q':
case '--quiet':
Log :: set_level('WARNING');
$log_level_set = true;
break;
case '--trace':
Log :: set_level('TRACE');
$log_level_set = true;
break;
default:
if (self :: $command)
$command_args[] = $args[$i];
else
self :: usage(
I18n::_(
"Invalid parameter \"%s\".\nNote: Command's parameter/argument must be place ".
"after the command."
), $args[$i]
);
}
}
}
if (!$log_level_set)
Log :: set_level('INFO');
if (!self :: $command)
self :: usage();
Log :: debug(
"Run %s command %s with argument(s) '%s'.",
basename($args[0]), self :: $command, implode("', '", $command_args)
);
try {
$result = call_user_func(self :: $commands[self :: $command]['handler'], $command_args);
exit($result?0:1);
}
catch(Exception $e) {
Log :: exception($e, I18n::_("An exception occured running command %s"), self :: $command);
exit(1);
}
}
/**
* Command to create new project based on EesyPHP framework
*
* @param array $command_args The command arguments
* @return void
*/
public static function cli_new_project($command_args) {
echo "This CLI tool permit to initialize a new project using the EesyPHP framework.\n";
readline('[Press enter to continue]');
echo "\n";
$root_path = null;
while (!$root_path) {
$root_path = getcwd();
$input = readline("Please enter the root directory of your new project [$root_path]: ");
if (empty($input))
break;
if (!is_dir($input))
echo "Invalid root directory specified: not found or is not a directory\n";
else if (!is_writeable($input))
echo "Invalid root directory specified: not writeable\n";
else
$root_path = $input;
}
$root_path = rtrim($root_path, "/");
$skel = __DIR__ . "/../skel";
foreach (
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($skel, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
) as $item
) {
$path = "$root_path/".$iterator->getSubPathname();
if ($item->isDir()) {
if (!mkdir($path))
die("Fail to create $path directory\n");
}
else {
if (!copy($item, $path))
die("Fail to copy file to $path\n");
}
}
echo "done. Start coding!\n";
}
}