From c4d9f8c89ec303ca25742319db393d93928755d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9EAFAK?= Date: Sat, 25 Feb 2023 03:25:03 +0300 Subject: [PATCH 1/2] 2.0 --- src/Application.php | 235 ++++++++++++++++++++++++++++++++++ src/Command.php | 41 ++++++ src/Console.php | 284 ------------------------------------------ src/Helpers.php | 45 +++++++ src/Input.php | 150 ++++++++++++++++++++++ src/InputArgument.php | 148 ++++++++++++++++++++++ src/Output.php | 173 +++++++++++++++++++++++++ src/Question.php | 91 ++++++++++++++ 8 files changed, 883 insertions(+), 284 deletions(-) create mode 100644 src/Application.php create mode 100644 src/Command.php delete mode 100644 src/Console.php create mode 100644 src/Helpers.php create mode 100644 src/Input.php create mode 100644 src/InputArgument.php create mode 100644 src/Output.php create mode 100644 src/Question.php diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 0000000..7e18d2e --- /dev/null +++ b/src/Application.php @@ -0,0 +1,235 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +class Application +{ + public const VERSION = '2.0'; + + /** @var string */ + protected $application; + + /** @var string */ + protected $version; + + protected $terminal; + + /** @var array */ + protected $commands = []; + + protected $input; + + protected $output; + + protected $command; + + public function __construct(string $application = 'InitPHP Console Application', string $version = '1.1') + { + $this->application = $application; + $this->version = $version; + } + + /** + * @param string $command + * @param callable|null $execute + * @param string $definition + * @return $this + * @throws \InvalidArgumentException + */ + public function register(string $command, callable $execute = null, string $definition = ''): self + { + if ($execute === null) { + if (!\class_exists($command)) { + throw new \InvalidArgumentException("The specified executable does not meet the requirements."); + } + $commandObj = new $command(); + if (!($commandObj instanceof Command)) { + throw new \InvalidArgumentException("The specified executable does not meet the requirements."); + } + if (!isset($commandObj->command)) { + throw new \Exception('The Command feature of the command class is undefined.'); + } + $command = $commandObj->command; + $execute = $commandObj; + $definition = $commandObj->definition(); + $help = $commandObj->help(); + if (empty($help)) { + $help = $definition; + } + } else { + $help = $definition; + } + if (!\is_callable($execute) && !($execute instanceof Command)) { + throw new \InvalidArgumentException("The specified executable does not meet the requirements."); + } + $this->commands[$command] = [ + 'command' => $command, + 'execute' => $execute, + 'definition' => $definition, + 'help' => $help, + ]; + + return $this; + } + + public function run(): bool + { + global $argv; + + $inputs = $argv; + $this->terminal = $inputs[0]; + \array_shift($inputs); + if (empty($inputs)) { + return false; + } + + $this->command = \current($inputs); + \array_shift($inputs); + + $this->input = new Input($inputs); + $this->output = new Output(); + + if ($this->command === 'help') { + $this->help($this->input, $this->output); + return true; + } + + if (isset($this->commands[$this->command])) { + + try { + $command = $this->commands[$this->command]; + + if ($this->input->hasArgument('help')) { + $this->commandHelp($this->input, $this->output, $command); + return true; + } + $execute = $command['execute']; + + if (\is_callable($execute)) { + \call_user_func_array($execute, [$this->input, $this->output]); + return true; + } + + if ($execute instanceof Command) { + $arguments = $execute->arguments(); + if (!empty($arguments)) { + foreach ($arguments as $argument) { + if ($argument instanceof InputArgument) { + if ($argument->run($this->input, $this->output) === FALSE) { + return false; + } + } else { + $this->output->error("One or more arguments are wrong! Please check that you create arguments from the InputArgument object."); + } + } + } + + $execute->execute($this->input, $this->output); + return true; + } + + + $this->output->error("The command is not executable; Please make sure the command is correctly recorded."); + return false; + } catch (\Throwable $e) { + $this->output->error($e->getMessage()); + return false; + } + + } else { + $this->output->error("The command was not found."); + } + return true; + } + + private function help(Input $input, Output $output) + { + $output->writeln(' + ____ _ __ ____ __ ______ ______ __ ___ ____ + / _/___ (_) /_/ __ \/ / / / __ \ / ____/___ ____ _________ / /__ |__ \ / __ \ + / // __ \/ / __/ /_/ / /_/ / /_/ / / / / __ \/ __ \/ ___/ __ \/ / _ \__/ / / / / / + _/ // / / / / /_/ ____/ __ / ____/ / /___/ /_/ / / / (__ ) /_/ / / __/ __/_/ /_/ / +/___/_/ /_/_/\__/_/ /_/ /_/_/ \____/\____/_/ /_/____/\____/_/\___/____(_)____/ + + '); + $output->writeln($this->application . ' v' . $this->version); + + $groups = []; + $ungroup = []; + foreach ($this->commands as $command) { + if (\strpos($command['command'], ':') === FALSE) { + $ungroup[$command['command']] = !empty($command['definition']) ? $command['definition'] : $command['help']; + continue; + } + $split = \explode(':', $command['command'], 2); + if (!isset($groups[$split[0]])) { + $groups[$split[0]] = []; + } + $groups[$split[0]][$command['command']] = !empty($command['definition']) ? $command['definition'] : $command['help']; + } + + $output->writeln(''); + $output->writeln('[COMMANDS]', [], [Output::COLOR_RED, Output::BOLD]); + foreach ($groups as $group_name => $group) { + $output->writeln(''); + $output->writeln($group_name, [], [Output::COLOR_MAGENTA]); + $output->list($group, 1); + } + $output->writeln(''); + $output->list($ungroup); + $output->writeln(''); + + $output->writeln('Copyright © 2022 - ' . date("Y") . ' InitPHP Console ' . self::VERSION); + $output->writeln('http://initphp.org'); + } + + private function commandHelp(Input $input, Output $output, array $command) + { + $output->writeln(\PHP_EOL . ($command['help'] ?? $command['definition'])); + $exe = $command['execute']; + if ($exe instanceof Command) { + $usageArguments = ['optional' => [], 'required' => []]; + if ($arguments = $exe->arguments()) { + $rows = []; + foreach ($arguments as $argument) { + if (!($argument instanceof InputArgument)) { + $output->error("One or more arguments are wrong! Please check that you create arguments from the InputArgument object."); + exit; + } + $rows[$argument->getName()] = $argument->getDefinition(); + if ($argument->isOptional()) { + $usageArguments['optional'][] = '[' . \trim($argument->getName() . '=(' . $argument->getType() . ')' . $argument->getDefault()) . ']'; + } else { + $usageArguments['required'][] = \trim($argument->getName() . '=(' . $argument->getType() . ')'.$argument->getDefault()); + } + } + $output->writeln(\PHP_EOL . "[PARAMETERS]"); + $output->list($rows, 1); + } + $usage = "[USAGE]" . \PHP_EOL . "\t"; + $usage .= 'php ' + . $this->terminal + . ' ' + . $exe->command + . ' ' + . \implode(' ', $usageArguments['required']) + . ' ' + . \implode(' ', $usageArguments['optional']); + $output->writeln(\trim($usage)); + } + } + +} diff --git a/src/Command.php b/src/Command.php new file mode 100644 index 0000000..f95726d --- /dev/null +++ b/src/Command.php @@ -0,0 +1,41 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +abstract class Command +{ + + /** @var string */ + public $command; + + abstract public function execute(Input $input, Output $output); + + public function help(): string + { + return 'No explanation for this command is specified.'; + } + + public function definition(): string + { + return ''; + } + + public function arguments(): array + { + return []; + } + +} diff --git a/src/Console.php b/src/Console.php deleted file mode 100644 index 4f50870..0000000 --- a/src/Console.php +++ /dev/null @@ -1,284 +0,0 @@ - - * @copyright Copyright © 2022 Muhammet ŞAFAK - * @license ./LICENSE MIT - * @version 1.0 - * @link https://www.muhammetsafak.com.tr - */ - -declare(strict_types=1); - -namespace InitPHP\Console; - -use function trim; -use function strlen; -use function str_repeat; -use function implode; -use function strtolower; -use function call_user_func_array; -use function array_search; -use function in_array; -use function fopen; -use function fgets; -use function fclose; -use function array_shift; -use function count; -use function substr; -use function ltrim; -use function strpos; -use function explode; -use function is_array; -use function is_object; -use function method_exists; -use function strtr; - -final class Console -{ - - public const COLOR_DEFAULT = 39; - public const COLOR_BLACK = 30; - public const COLOR_RED = 31; - public const COLOR_GREEN = 32; - public const COLOR_YELLOW = 33; - public const COLOR_BLUE = 34; - public const COLOR_MAGENTA = 35; - public const COLOR_CYAN = 36; - public const COLOR_LIGHT_GRAY = 37; - public const COLOR_DARK_GRAY = 90; - public const COLOR_LIGHT_RED = 91; - public const COLOR_LIGHT_GREEN = 92; - public const COLOR_LIGHT_YELLOW = 93; - public const COLOR_LIGHT_BLUE = 94; - public const COLOR_LIGHT_MAGENTA= 95; - public const COLOR_LIGHT_CYAN = 96; - public const COLOR_WHITE = 97; - - public const BACKGROUND_BLACK = 40; - public const BACKGROUND_RED = 41; - public const BACKGROUND_GREEN = 42; - public const BACKGROUND_YELLOW = 43; - public const BACKGROUND_BLUE = 44; - public const BACKGROUND_MAGENTA = 45; - public const BACKGROUND_CYAN = 46; - - public const ITALIC = 3; - public const BOLD = 1; - public const UNDERLINE = 4; - public const STRIKETHROUGH = 9; - - - /** @var null|string */ - protected $command = null; - - /** @var array */ - protected $commands = []; - - /** @var array */ - protected $flags = []; - - /** @var array */ - protected $segments = []; - - /** @var array */ - protected $helps = []; - - public function __construct() - { - $this->setUp(); - - $this->register('help', function (Console $console) { - $outputs = []; $max_command_len = 0; - unset($console->helps['help']); - foreach ($console->helps as $key => $value) { - if(empty($value)){ - $value = "\e[3mNo description has been written for this command.\e[0m"; - } - $outputs[$key] = trim($value); - $len = strlen($key); - if($max_command_len < $len){ - $max_command_len = $len; - } - } - foreach ($outputs as $key => $value) { - $space_size = $max_command_len - strlen($key); - $message = "\e[1m" . $key . "\e[0m" - . str_repeat(' ', $space_size) - . ' : ' . $value; - $console->message($message . \PHP_EOL); - } - }); - } - - public function message(string $msg, array $context = array(), array $format = [self::COLOR_DEFAULT]): void - { - echo "\e[" . implode(';', $format) . 'm' . $this->interpolate($msg, $context) . "\e[0m"; - } - - public function error(string $msg, array $context = array()): void - { - $this->message('[ERROR] ' . $msg, $context, [self::COLOR_WHITE, self::BOLD, self::BACKGROUND_RED]); - } - - public function success(string $msg, array $context = array()): void - { - $this->message('[SUCCESS] ' . $msg, $context, [self::COLOR_WHITE, self::BACKGROUND_GREEN, self::BOLD]); - } - - public function warning(string $msg, array $context = array()): void - { - $this->message('[WARNING] ' . $msg, $context, [self::COLOR_WHITE, self::BACKGROUND_YELLOW, self::BOLD]); - } - - public function info(string $msg, array $context = array()): void - { - $this->message('[INFO] ' . $msg, $context, [self::COLOR_CYAN]); - } - - /** - * @param string $command - * @param \Callable $execute - * @param string $definition - * @return $this - */ - public function register(string $command, callable $execute, string $definition = ''): Console - { - $lowercase = strtolower($command); - $this->commands[$lowercase] = $execute; - $this->helps[$lowercase] = $definition; - return $this; - } - - public function run(): bool - { - if($this->command === null){ - $this->error('A command to run was not found.'); - return false; - } - $lowercase = strtolower($this->command); - if(!isset($this->commands[$lowercase])){ - $this->error('A command to run is not defined.'); - return false; - } - call_user_func_array($this->commands[$lowercase], [$this]); - return true; - } - - public function segment(int $id = 0, $default = null) - { - return $this->segments[$id] ?? $default; - } - - public function segments(): array - { - return $this->segments; - } - - /** - * @return false|int - */ - public function search_segment(string $search) - { - return array_search($search, $this->segments, true); - } - - public function has_segment(string $search): bool - { - return in_array($search, $this->segments, true); - } - - public function has_flag(string $name): bool - { - $lowercase = strtolower($name); - return isset($this->flags[$lowercase]); - } - - public function flag(string $name, $default = null) - { - $lowercase = strtolower($name); - return $this->flags[$lowercase] ?? $default; - } - - public function flags(): array - { - return $this->flags; - } - - public function ask(string $question): string - { - $this->message( \PHP_EOL . $question . \PHP_EOL); - $handle = fopen("php://stdin", "r"); - do { - $line = fgets($handle); - } while ($line == ''); - fclose($handle); - return trim((string)$line); - } - - /** - * @return void - */ - protected function setUp(): void - { - global $argv; - array_shift($argv); - if(empty($argv)){ - return; - } - $this->command = $argv[0]; - array_shift($argv); - $this->segments = $argv; - for ($i = 0; $i < count($this->segments); ++$i) { - if(!isset($this->segments[$i])){ - continue; - } - $segment = $this->segments[$i]; - if (substr($segment, 0, 1) === '-') { - $segment = ltrim($segment, '-'); - if(strpos($segment, '=')){ - $parse = explode('=', $segment, 2); - $lowercase = strtolower($parse[0]); - $this->flags[$lowercase] = $parse[1]; - }else{ - $valueId = $i + 1; - $lowercase = strtolower($segment); - if(!isset($this->segments[$valueId])){ - $this->flags[$lowercase] = ''; - continue; - } - if(substr($this->segments[$valueId], 0, 1) === '-') { - $this->flags[$lowercase] = ''; - continue; - } - $this->flags[$lowercase] = $this->segments[$valueId]; - ++$i; - continue; - } - } - } - } - - /** - * @param string $message - * @param array $context - * @return string - */ - private function interpolate(string $message, array $context = []): string - { - if(empty($context)){ - return $message; - } - $replace = []; - foreach ($context as $key => $val) { - if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { - $replace['{' . $key . '}'] = (string)$val; - } - } - return strtr($message, $replace); - } - -} diff --git a/src/Helpers.php b/src/Helpers.php new file mode 100644 index 0000000..dcccb88 --- /dev/null +++ b/src/Helpers.php @@ -0,0 +1,45 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +final class Helpers +{ + + public static function strValueCast(string $value) + { + switch (\strtolower($value)) { + case '': + return ''; + case 'null': + return null; + case 'yes': + case 'true': + return true; + case 'no': + case 'false': + return false; + default: + if ((bool)\preg_match('/^[-|+]*[0-9]+$/', $value)) { + return \intval($value); + } + if ((bool)\preg_match('/^[-|+]*[0-9]+[\.|,]{1}[0-9]+$/', $value)) { + return \floatval(\strtr($value, [','=>'.'])); + } + return $value; + } + } + +} diff --git a/src/Input.php b/src/Input.php new file mode 100644 index 0000000..66a84a4 --- /dev/null +++ b/src/Input.php @@ -0,0 +1,150 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +class Input +{ + + protected $argv; + + protected $arguments = []; + + protected $options = []; + + protected $segments = []; + + public function __construct(array $argv) + { + $this->argv = $argv; + for ($i = 0; $i < \count($this->argv); ++$i) { + if (!isset($this->argv[$i])) { + continue; + } + $segment = $this->argv[$i]; + if (\trim($segment, " \t\n\r\0\x0B-") === '') { + continue; + } + if (\substr($segment, 0, 2) == '--') { + /** ARGUMENTS */ + $seg = \ltrim($segment, '-'); + if (\strpos($seg, '=') !== FALSE) { + $split = \explode('=', $seg, 2); + $this->arguments[$split[0]] = Helpers::strValueCast($split[1]); + } else { + $this->arguments[$seg] = ''; + } + continue; + } + + if (\substr($segment, 0, 1) == '-') { + /** OPTIONS */ + $seg = \ltrim($segment, '-'); + if (\strpos($seg, '=') === FALSE) { + !isset($this->options[$seg]) && $this->options[$seg] = ''; + $options = \preg_split('//', $seg, -1, \PREG_SPLIT_NO_EMPTY); + foreach ($options as $option) { + $this->options[$option] = $option; + } + } else { + $split = \explode('=', $seg, 2); + $this->options[$split[0]] = Helpers::strValueCast($split[1]); + } + continue; + } + + $this->segments[] = Helpers::strValueCast($segment); + } + } + + /** + * @param string $name + * @return bool + */ + public function hasArgument(string $name): bool + { + return \array_key_exists($name, $this->arguments); + } + + /** + * @param string $name + * @param mixed $default + * @return mixed|null + */ + public function getArgument(string $name, $default = null) + { + return \array_key_exists($name, $this->arguments) ? $this->arguments[$name] : $default; + } + + /** + * @return array + */ + public function allArguments(): array + { + return $this->arguments; + } + + /** + * @param int $index + * @return bool + */ + public function hasSegment(int $index): bool + { + return \array_key_exists($index, $this->segments); + } + + /** + * @param int $index + * @param mixed $default + * @return mixed + */ + public function getSegment(int $index, $default = null) + { + return \array_key_exists($index, $this->segments) ? $this->segments[$index] : $default; + } + + /** + * @return array + */ + public function allSegment(): array + { + return $this->segments; + } + + /** + * @param string $name + * @return bool + */ + public function hasOption(string $name): bool + { + return array_key_exists($name, $this->options); + } + + /** + * @param string $name + * @param $default + * @return mixed|null + */ + public function getOption(string $name, $default = null) + { + return \array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + public function importArguments(array ...$arguments) + { + $this->arguments = \array_merge($this->arguments, ...$arguments); + } + +} diff --git a/src/InputArgument.php b/src/InputArgument.php new file mode 100644 index 0000000..a6ad2ed --- /dev/null +++ b/src/InputArgument.php @@ -0,0 +1,148 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +final class InputArgument +{ + + public const ANY = 'ANY'; + public const INT = 'INT'; + public const FLOAT = 'FLOAT'; + public const NUMERIC = 'NUMBER'; + public const BOOL = 'BOOL'; + public const STR = 'STRING'; + + protected const SUPPORTED_TYPES = [ + self::ANY, self::INT, self::FLOAT, self::NUMERIC, self::BOOL, self::STR, + ]; + + protected $name; + + protected $definition; + + protected $isOptional = true; + + protected $default; + + protected $type; + + public function __construct(string $name, $type, $default, bool $isOptional = true, string $definition = '') + { + $this->name = $name; + if (!\in_array($type, self::SUPPORTED_TYPES, true)) { + throw new \InvalidArgumentException('The species defined for the --' . $this->name . ' parameter is not supported.'); + } + $this->type = $type; + if (!$this->valueCheck($default)) { + throw new \InvalidArgumentException('The default value for the --' . $this->name . ' parameter must be a type accepted for the parameter.'); + } + $this->default = $default; + $this->isOptional = $isOptional; + $this->definition = $definition; + } + + public function isOptional(): bool + { + return $this->isOptional; + } + + public function getType(): string + { + return $this->type; + } + + public function getName(): string + { + return '--' . $this->name; + } + + public function getDefinition(): string + { + return $this->definition; + } + + public function getDefault(): ?string + { + return isset($this->default) ? (string)$this->default : null; + } + + public function run(Input &$input, Output &$output): bool + { + if ($input->hasArgument($this->name)) { + $value = $input->getArgument($this->name); + } elseif ($input->hasOption($this->name)) { + $value = $input->getOption($this->name); + } else { + if ($this->isOptional === FALSE) { + $output->error('The --' . $this->name . ' parameter is undefined.'); + return false; + } else { + $input->importArguments([$this->name => $this->default]); + return true; + } + } + + if (!$this->valueCheck($value)) { + if ($this->isOptional === FALSE) { + $output->error('The value given for the --' . $this->name . ' parameter is invalid.'); + return false; + } else { + $value = $this->default; + } + } else { + if (empty($value) && $this->isOptional === FALSE && isset($this->default)) { + $value = $this->default; + } + } + $input->importArguments([$this->name => $value]); + return true; + } + + private function valueCheck(&$value): bool + { + if ($this->type === self::ANY) { + return true; + } + if ($this->type === self::BOOL) { + if (\is_bool($value)) { + return true; + } + if (\in_array($value, [0, 1])) { + $value = (bool)$value; + return true; + } + return false; + } + if ($this->type === self::INT) { + return \is_int($value); + } + if ($this->type === self::FLOAT) { + return \is_float($value); + } + if ($this->type === self::NUMERIC) { + return \is_numeric($value); + } + if ($this->type === self::STR) { + if (\is_numeric($value) || \is_bool($value)) { + $value = (string)$value; + } + return \is_string($value); + } + return false; + } + + +} diff --git a/src/Output.php b/src/Output.php new file mode 100644 index 0000000..5e7037d --- /dev/null +++ b/src/Output.php @@ -0,0 +1,173 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +class Output +{ + public const COLOR_DEFAULT = 39; + public const COLOR_BLACK = 30; + public const COLOR_RED = 31; + public const COLOR_GREEN = 32; + public const COLOR_YELLOW = 33; + public const COLOR_BLUE = 34; + public const COLOR_MAGENTA = 35; + public const COLOR_CYAN = 36; + public const COLOR_LIGHT_GRAY = 37; + public const COLOR_DARK_GRAY = 90; + public const COLOR_LIGHT_RED = 91; + public const COLOR_LIGHT_GREEN = 92; + public const COLOR_LIGHT_YELLOW = 93; + public const COLOR_LIGHT_BLUE = 94; + public const COLOR_LIGHT_MAGENTA= 95; + public const COLOR_LIGHT_CYAN = 96; + public const COLOR_WHITE = 97; + + public const BACKGROUND_BLACK = 40; + public const BACKGROUND_RED = 41; + public const BACKGROUND_GREEN = 42; + public const BACKGROUND_YELLOW = 43; + public const BACKGROUND_BLUE = 44; + public const BACKGROUND_MAGENTA = 45; + public const BACKGROUND_CYAN = 46; + + public const ITALIC = 3; + public const BOLD = 1; + public const UNDERLINE = 4; + public const STRIKETHROUGH = 9; + + function progressBar($done, $total) { + + if (!\is_numeric($done) || !\is_numeric($total)) { + throw new \InvalidArgumentException('\$done and \$total can be integer or float.'); + } + + $progress = \floor(($done / $total) * 100); + $done_progress = 100 - $progress; + + \fwrite(\STDOUT, \sprintf("\033[0G\033[2K[%'=" . $progress . "s>%-" . $done_progress . "s] - " .$progress ."%% ". $done . "/" . $total, "", "")); + } + + public function list(array $rows, int $leftSpace = 0) + { + $space = 1; + + foreach ($rows as $key => $value) { + if (!\is_string($key)) { + continue; + } + $rowSpace = (int)\ceil(strlen($key) / 8); + if ($space < $rowSpace) { + $space = $rowSpace; + } + } + + $output = ''; + $lineStart = $leftSpace > 0 ? \str_repeat("\t", $leftSpace) : ''; + foreach ($rows as $key => $value) { + if ($value === \PHP_EOL) { + $output .= $value; + continue; + } + $line = $lineStart + . $key + . \str_repeat("\t", $space) + . ': ' + . $value; + $output .= $line . \PHP_EOL; + } + \fwrite(\STDOUT, $output); + } + + public function ask(string $question, bool $empty = true) + { + $this->write($question, [], true); + + do { + $input = Helpers::strValueCast(\trim(\fgets(\STDIN))); + if (\in_array($input, ['exit', 'quit'])) { + exit; + } + } while ($empty === FALSE && $input == ''); + + return $input; + } + + public function question(Question $question) + { + $this->write($question->getQuestion(), [], true); + + do { + $answer = \trim(\fgets(\STDIN)); + if ($question->hasOption($answer)) { + return Helpers::strValueCast($answer); + } + if (\in_array($answer, ['exit', 'quit'])) { + exit; + } + if ($question->isOptional()) { + $default = $question->getDefault(); + if ($default !== '__Qu€sti0nN0D€f@ultV@lue__') { + return $default; + } else { + return $answer; + } + } + } while (true); + } + + public function write(string $str, array $context = [], bool $newLine = false, array $format = [self::COLOR_DEFAULT]): void + { + if (!empty($context)) { + $replace = []; + foreach ($context as $key => $val) { + if (!\is_array($val) && (!\is_object($val) || \method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = (string)$val; + } + } + $str = \strtr($str, $replace); + } + + $msg = "\e[" . \implode(';', $format) . "m" . $str . "\e[0m" + . ($newLine !== FALSE ? \PHP_EOL : ''); + \fwrite(\STDOUT, $msg); + } + + public function writeln(string $str, array $context = [], array $format = [self::COLOR_DEFAULT]): void + { + $this->write($str, $context, true, $format); + } + + public function error(string $msg, array $context = array()): void + { + $this->writeln('[ERROR] ' . $msg, $context, [self::COLOR_WHITE, self::BOLD, self::BACKGROUND_RED]); + } + + public function success(string $msg, array $context = array()): void + { + $this->writeln('[SUCCESS] ' . $msg, $context, [self::COLOR_WHITE, self::BACKGROUND_GREEN, self::BOLD]); + } + + public function warning(string $msg, array $context = array()): void + { + $this->writeln('[WARNING] ' . $msg, $context, [self::COLOR_WHITE, self::BACKGROUND_YELLOW, self::BOLD]); + } + + public function info(string $msg, array $context = array()): void + { + $this->writeln('[INFO] ' . $msg, $context, [self::COLOR_CYAN]); + } + +} diff --git a/src/Question.php b/src/Question.php new file mode 100644 index 0000000..fe1c4d0 --- /dev/null +++ b/src/Question.php @@ -0,0 +1,91 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\Console; + +class Question +{ + + protected $question; + + protected $options = [true, false]; + + protected $isOptional = false; + + protected $default; + + public function isOptional(): bool + { + return $this->isOptional; + } + + public function optional(): self + { + $this->isOptional = true; + + return $this; + } + + public function notOptional(): self + { + $this->isOptional = false; + + return $this; + } + + public function hasOption(string $option): bool + { + return \in_array($option, $this->options); + } + + public function setOptions(array $options): self + { + $this->options = $options; + + return $this; + } + + public function addOption(string $option, $value): self + { + $this->options[] = $value; + + return $this; + } + + public function setQuestion(string $question): self + { + $this->question = $question; + + return $this; + } + + public function getQuestion(): string + { + return $this->question ?? ''; + } + + public function setDefault($default): self + { + $this->default = $default; + + return $this; + } + + public function getDefault() + { + return $this->default ?? '__Qu€sti0nN0D€f@ultV@lue__'; + } + +} From 41e37b4aae3d1c49b292a74feeb6ea6423f54794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9EAFAK?= Date: Sat, 25 Feb 2023 03:29:56 +0300 Subject: [PATCH 2/2] Updated README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9002df4..5089a0d 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,20 @@ composer require initphp/console ```php #!usr/bin/php require_once __DIR__ . '/../vendor/autoload.php'; -use InitPHP\Console\Console; +use \InitPHP\Console\{Application, Input, Output}; -$console = new Console(); +$console = new Application("My Console Application", '1.0'); // Register commands ... // hello -name=John -$console->register('hello', function (Console $console) { - if ($console->has_flag('name')) { - $console->message("Hello {name}", [ - 'name' => $console->flag('name') +$console->register('hello', function (Input $input, Output $output) { + if ($input->hasArgument('name')) { + $output->writeln('Hello {name}', [ + 'name' => $input->getArgument('name') ]); - }else{ - $console->message('Hello World!'); + } else { + $output->writeln('Hello World!'); } }, 'Says hello.');