GitHub
Tests: 12 • Commercial: 2 • Pet projects: 4 • Legacy: 4
Total: 22

.NET Framework

Test
2021

Project Request

ASP.NET MVC • C# • SQL Server
Idea of the project: if someone wants to order a project development, here you can send an application.
Test
2020

ProjectC

ASP.NET MVC • C# • JSON • jQuery
JSON data processing.
Test
2020

Vehicle Maintenance

ASP.NET MVC • VB.NET • JSON
Idea of the project: if someone wants to order a project development, here you can send an application.
Test
2019

Movie Navigator

ASP.NET MVC • VB.NET
Request information about movie from IMDB.
Test
2018

Customers Exchange

ASP.NET MVC • C# • SQL Server
Automated teller machine emulation.
Test
2016

ATM

ASP.NET MVC • C#
Automated teller machine emulation.

.NET Core

Pet project
2022

Mail Daemon

.NET 9 • Console • JSON
Utility to send mails with customizable settings.

Custom

Code
2024

Buns of code

.NET Framework • C# • JavaScript
Code snippets from my projects, ready to use; tiny tests; code examples.

PHP

Test
2024

Mediabox

PHP 8 • Laravel 11 • Vue.js • Composer • SQLite
Test project for media files management.
Test
2020

Loan Castle

PHP • MariaDB
Jums jāizstrādā kāda lielāk projekta prototips. Izstrādājot prototipu, paturiet prātā, ka projektam attīstoties, šo prototipu varētu vajadzēt pilnveidot.
Test
2020

Content Management

PHP • MySQL • AJAX
Создать простой сайт, где будет страница с формой для авторизации и страница для авторизованного пользователя.
Test
2019

Laravel

PHP • Laravel • Vue.js • Composer • SQLite
Izveidot aplikāciju, kura ik pēc noteikta intervāla (60 sekundes) veic ierakstu datubāzē izmantojot Laravel freimworka iebūvēto funkcionalitāti.
Test
2019

Phone Check

PHP • JavaScript • JSON • Docker
Implement application to detect country by phone number.

Frontend

Test
2021

Forex Wall

npm • React
For this exercise, what we need is a simple live wall for tracking currencies.

Business projects

Commercial
2008

Certification Center

.NET Framework 4.8 • ASP.NET Web Forms • C# • LINQ • SQL Server • ADO.NET • Dapper • JavaScript • jQuery • Git
Transport registration and certification services in Latvia, Customer Relationship Management.
Commercial
2000

Amerikas Auto

.NET Framework 4.8 • ASP.NET Web Forms • C# • LINQ • SQL Server • ADO.NET • Entity Framework • JavaScript • jQuery • Git
Car service and spare parts for all USA and European car models, Customer Relationship Management.

Pet projects

Pet project
2023

Geolocation Assistant

.NET 8 • ASP.NET Core • C# • Web API • JSON • Git
Website for determining geolocation by IP or geotagged photo.
Pet project
2008

Web Dynamics

.NET Framework 4.8 • ASP.NET Web Forms • C# • LINQ • Web API • JSON • SQL Server • Dapper • JavaScript • jQuery • SVG • Git
Software development blog. Articles, books, videos, content management.
Pet project
2000

Blackball

.NET Framework 4.8 • ASP.NET Web Forms • C# • LINQ • Web API • JSON • XML • SQL Server • Dapper • JavaScript • jQuery • SVG • Git
My entertainment portal created from scratch.

Good old times

Legacy
2000

DOS Clock

Turbo Pascal • Assembler
Digital clock.
Legacy
2000

BrainOut

Turbo Pascal • Assembler
Tank battle game.
Legacy
1999

Airport Administrator

Turbo Pascal
Курсовая работа в институте.
Legacy
1998

Atomizer

Turbo Pascal • Assembler
Atomizer, aka «Studio2D». Graphic raster editor. AGI is my own «Atomizer Generated Image» file format.

Mediabox

2024 Test

Test project for media files management.

PHP 8 Laravel 11 Vue.js Composer SQLite
Information
Source code
  app
  Http
  .bin
  vite
  lib
  dist
  esm
  node
  lib
  core
  env
  node
  lib
  lib
  lib
  lib
  dist
  lib
  lib
  dist
  es
  lib
  vite
  dist
  node
  dist
  css
  js
  app
  data
  logs
  Unit
  math
  src
  src
  src
  docs
  en
  lib
  src
  src
  Cron
  src
  src
  Core
  ORM
  Spot
  filp
  src
  css
  js
  Util
  src
  src
  src
  src
  psr7
  src
  src
  Core
  Text
  Type
  Xml
  Core
  Text
  Type
  Xml
  src
  Auth
  make
  Bus
  View
  Auth
  Bus
  Http
  Mail
  View
  Auth
  Bus
  Http
  dist
  Http
  Json
  Log
  Mail
  html
  text
  Jobs
  lang
  en
  View
  pint
  pint
  src
  sail
  8.0
  8.1
  8.2
  8.3
  src
  src
  src
  src
  Node
  Util
  Node
  Node
  Node
  Data
  Node
  Node
  Util
  Xml
  src
  src
  src
  docs
  Pass
  src
  Curl
  Test
  src
  Date
  Spl
  lazy
  src
  Cli
  Lang
  List
  src
  src
  lib
  Node
  Expr
  Cast
  Name
  Stmt
  src
  src
  Html
  src
  xml
  src
  src
  src
  Data
  Node
  Html
  css
  js
  Xml
  Util
  src
  src
  src
  src
  src
  Test
  Test
  Math
  Type
  Api
  Rule
  Stub
  Api
  PHPT
  Cli
  Xml
  Util
  PHP
  Xml
  psr
  src
  src
  src
  src
  src
  docs
  src
  log
  src
  src
  psy
  src
  Hoa
  77
  78
  Sudo
  Util
  src
  src
  Map
  Tool
  uuid
  src
  Time
  Guid
  Lazy
  Math
  Dce
  Node
  Time
  Type
  src
  src
  src
  src
  src
  diff
  src
  src
  src
  src
  src
  src
  src
  src
  type
  src
  type
  src
  Test
  CI
  Node
  css
  js
  File
  Test
  Log
  Test
  Smtp
  Auth
  mime
  Part
  Test
  Test
  data
  data
  Test
  Util
  Test
  uid
  css
  js
  Test
  yaml
  Tag
  src
  src
  Css
  Rule
  src
  File
  Util
  voku
  src
  voku
  data
  src
  .env
Root / vendor / symfony / console / Application.php
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\CompleteCommand; use Symfony\Component\Console\Command\DumpCompletionCommand; use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\LazyCommand; use Symfony\Component\Console\Command\ListCommand; use Symfony\Component\Console\Command\SignalableCommandInterface; use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Completion\Suggestion; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\Console\Event\ConsoleSignalEvent; use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Exception\NamespaceNotFoundException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\DebugFormatterHelper; use Symfony\Component\Console\Helper\DescriptorHelper; use Symfony\Component\Console\Helper\FormatterHelper; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\ProcessHelper; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputAwareInterface; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\SignalRegistry\SignalRegistry; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\ErrorHandler\ErrorHandler; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Service\ResetInterface; /** * An Application is the container for a collection of commands. * * It is the main entry point of a Console application. * * This class is optimized for a standard CLI environment. * * Usage: * * $app = new Application('myapp', '1.0 (stable)'); * $app->add(new SimpleCommand()); * $app->run(); * * @author Fabien Potencier <fabien@symfony.com> */ class Application implements ResetInterface { private array $commands = []; private bool $wantHelps = false; private ?Command $runningCommand = null; private ?CommandLoaderInterface $commandLoader = null; private bool $catchExceptions = true; private bool $catchErrors = false; private bool $autoExit = true; private InputDefinition $definition; private HelperSet $helperSet; private ?EventDispatcherInterface $dispatcher = null; private Terminal $terminal; private string $defaultCommand; private bool $singleCommand = false; private bool $initialized = false; private ?SignalRegistry $signalRegistry = null; private array $signalsToDispatchEvent = []; public function __construct( private string $name = 'UNKNOWN', private string $version = 'UNKNOWN', ) { $this->terminal = new Terminal(); $this->defaultCommand = 'list'; if (\defined('SIGINT') && SignalRegistry::isSupported()) { $this->signalRegistry = new SignalRegistry(); $this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2]; } } /** * @final */ public function setDispatcher(EventDispatcherInterface $dispatcher): void { $this->dispatcher = $dispatcher; } public function setCommandLoader(CommandLoaderInterface $commandLoader): void { $this->commandLoader = $commandLoader; } public function getSignalRegistry(): SignalRegistry { if (!$this->signalRegistry) { throw new RuntimeException('Signals are not supported. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); } return $this->signalRegistry; } public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void { $this->signalsToDispatchEvent = $signalsToDispatchEvent; } /** * Runs the current application. * * @return int 0 if everything went fine, or an error code * * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. */ public function run(?InputInterface $input = null, ?OutputInterface $output = null): int { if (\function_exists('putenv')) { @putenv('LINES='.$this->terminal->getHeight()); @putenv('COLUMNS='.$this->terminal->getWidth()); } $input ??= new ArgvInput(); $output ??= new ConsoleOutput(); $renderException = function (\Throwable $e) use ($output) { if ($output instanceof ConsoleOutputInterface) { $this->renderThrowable($e, $output->getErrorOutput()); } else { $this->renderThrowable($e, $output); } }; if ($phpHandler = set_exception_handler($renderException)) { restore_exception_handler(); if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { $errorHandler = true; } elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) { $phpHandler[0]->setExceptionHandler($errorHandler); } } $this->configureIO($input, $output); try { $exitCode = $this->doRun($input, $output); } catch (\Throwable $e) { if ($e instanceof \Exception && !$this->catchExceptions) { throw $e; } if (!$e instanceof \Exception && !$this->catchErrors) { throw $e; } $renderException($e); $exitCode = $e->getCode(); if (is_numeric($exitCode)) { $exitCode = (int) $exitCode; if ($exitCode <= 0) { $exitCode = 1; } } else { $exitCode = 1; } } finally { // if the exception handler changed, keep it // otherwise, unregister $renderException if (!$phpHandler) { if (set_exception_handler($renderException) === $renderException) { restore_exception_handler(); } restore_exception_handler(); } elseif (!$errorHandler) { $finalHandler = $phpHandler[0]->setExceptionHandler(null); if ($finalHandler !== $renderException) { $phpHandler[0]->setExceptionHandler($finalHandler); } } } if ($this->autoExit) { if ($exitCode > 255) { $exitCode = 255; } exit($exitCode); } return $exitCode; } /** * Runs the current application. * * @return int 0 if everything went fine, or an error code */ public function doRun(InputInterface $input, OutputInterface $output): int { if (true === $input->hasParameterOption(['--version', '-V'], true)) { $output->writeln($this->getLongVersion()); return 0; } try { // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. $input->bind($this->getDefinition()); } catch (ExceptionInterface) { // Errors must be ignored, full binding/validation happens later when the command is known. } $name = $this->getCommandName($input); if (true === $input->hasParameterOption(['--help', '-h'], true)) { if (!$name) { $name = 'help'; $input = new ArrayInput(['command_name' => $this->defaultCommand]); } else { $this->wantHelps = true; } } if (!$name) { $name = $this->defaultCommand; $definition = $this->getDefinition(); $definition->setArguments(array_merge( $definition->getArguments(), [ 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), ] )); } try { $this->runningCommand = null; // the command name MUST be the first element of the input $command = $this->find($name); } catch (\Throwable $e) { if (($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) && 1 === \count($alternatives = $e->getAlternatives()) && $input->isInteractive()) { $alternative = $alternatives[0]; $style = new SymfonyStyle($input, $output); $output->writeln(''); $formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true); $output->writeln($formattedBlock); if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) { if (null !== $this->dispatcher) { $event = new ConsoleErrorEvent($input, $output, $e); $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); return $event->getExitCode(); } return 1; } $command = $this->find($alternative); } else { if (null !== $this->dispatcher) { $event = new ConsoleErrorEvent($input, $output, $e); $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); if (0 === $event->getExitCode()) { return 0; } $e = $event->getError(); } try { if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) { $helper = new DescriptorHelper(); $helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [ 'format' => 'txt', 'raw_text' => false, 'namespace' => $namespace, 'short' => false, ]); return isset($event) ? $event->getExitCode() : 1; } throw $e; } catch (NamespaceNotFoundException) { throw $e; } } } if ($command instanceof LazyCommand) { $command = $command->getCommand(); } $this->runningCommand = $command; $exitCode = $this->doRunCommand($command, $input, $output); $this->runningCommand = null; return $exitCode; } public function reset(): void { } public function setHelperSet(HelperSet $helperSet): void { $this->helperSet = $helperSet; } /** * Get the helper set associated with the command. */ public function getHelperSet(): HelperSet { return $this->helperSet ??= $this->getDefaultHelperSet(); } public function setDefinition(InputDefinition $definition): void { $this->definition = $definition; } /** * Gets the InputDefinition related to this Application. */ public function getDefinition(): InputDefinition { $this->definition ??= $this->getDefaultInputDefinition(); if ($this->singleCommand) { $inputDefinition = $this->definition; $inputDefinition->setArguments(); return $inputDefinition; } return $this->definition; } /** * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). */ public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ( CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && 'command' === $input->getCompletionName() ) { foreach ($this->all() as $name => $command) { // skip hidden commands and aliased commands as they already get added below if ($command->isHidden() || $command->getName() !== $name) { continue; } $suggestions->suggestValue(new Suggestion($command->getName(), $command->getDescription())); foreach ($command->getAliases() as $name) { $suggestions->suggestValue(new Suggestion($name, $command->getDescription())); } } return; } if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) { $suggestions->suggestOptions($this->getDefinition()->getOptions()); return; } } /** * Gets the help message. */ public function getHelp(): string { return $this->getLongVersion(); } /** * Gets whether to catch exceptions or not during commands execution. */ public function areExceptionsCaught(): bool { return $this->catchExceptions; } /** * Sets whether to catch exceptions or not during commands execution. */ public function setCatchExceptions(bool $boolean): void { $this->catchExceptions = $boolean; } /** * Sets whether to catch errors or not during commands execution. */ public function setCatchErrors(bool $catchErrors = true): void { $this->catchErrors = $catchErrors; } /** * Gets whether to automatically exit after a command execution or not. */ public function isAutoExitEnabled(): bool { return $this->autoExit; } /** * Sets whether to automatically exit after a command execution or not. */ public function setAutoExit(bool $boolean): void { $this->autoExit = $boolean; } /** * Gets the name of the application. */ public function getName(): string { return $this->name; } /** * Sets the application name. */ public function setName(string $name): void { $this->name = $name; } /** * Gets the application version. */ public function getVersion(): string { return $this->version; } /** * Sets the application version. */ public function setVersion(string $version): void { $this->version = $version; } /** * Returns the long version of the application. */ public function getLongVersion(): string { if ('UNKNOWN' !== $this->getName()) { if ('UNKNOWN' !== $this->getVersion()) { return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion()); } return $this->getName(); } return 'Console Tool'; } /** * Registers a new command. */ public function register(string $name): Command { return $this->add(new Command($name)); } /** * Adds an array of command objects. * * If a Command is not enabled it will not be added. * * @param Command[] $commands An array of commands */ public function addCommands(array $commands): void { foreach ($commands as $command) { $this->add($command); } } /** * Adds a command object. * * If a command with the same name already exists, it will be overridden. * If the command is not enabled it will not be added. */ public function add(Command $command): ?Command { $this->init(); $command->setApplication($this); if (!$command->isEnabled()) { $command->setApplication(null); return null; } if (!$command instanceof LazyCommand) { // Will throw if the command is not correctly initialized. $command->getDefinition(); } if (!$command->getName()) { throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command))); } $this->commands[$command->getName()] = $command; foreach ($command->getAliases() as $alias) { $this->commands[$alias] = $command; } return $command; } /** * Returns a registered command by name or alias. * * @throws CommandNotFoundException When given command name does not exist */ public function get(string $name): Command { $this->init(); if (!$this->has($name)) { throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); } // When the command has a different name than the one used at the command loader level if (!isset($this->commands[$name])) { throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name)); } $command = $this->commands[$name]; if ($this->wantHelps) { $this->wantHelps = false; $helpCommand = $this->get('help'); $helpCommand->setCommand($command); return $helpCommand; } return $command; } /** * Returns true if the command exists, false otherwise. */ public function has(string $name): bool { $this->init(); return isset($this->commands[$name]) || ($this->commandLoader?->has($name) && $this->add($this->commandLoader->get($name))); } /** * Returns an array of all unique namespaces used by currently registered commands. * * It does not return the global namespace which always exists. * * @return string[] */ public function getNamespaces(): array { $namespaces = []; foreach ($this->all() as $command) { if ($command->isHidden()) { continue; } $namespaces[] = $this->extractAllNamespaces($command->getName()); foreach ($command->getAliases() as $alias) { $namespaces[] = $this->extractAllNamespaces($alias); } } return array_values(array_unique(array_filter(array_merge([], ...$namespaces)))); } /** * Finds a registered namespace by a name or an abbreviation. * * @throws NamespaceNotFoundException When namespace is incorrect or ambiguous */ public function findNamespace(string $namespace): string { $allNamespaces = $this->getNamespaces(); $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*'; $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); if (!$namespaces) { $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { if (1 == \count($alternatives)) { $message .= "\n\nDid you mean this?\n "; } else { $message .= "\n\nDid you mean one of these?\n "; } $message .= implode("\n ", $alternatives); } throw new NamespaceNotFoundException($message, $alternatives); } $exact = \in_array($namespace, $namespaces, true); if (\count($namespaces) > 1 && !$exact) { throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); } return $exact ? $namespace : reset($namespaces); } /** * Finds a command by name or alias. * * Contrary to get, this command tries to find the best * match if you give it an abbreviation of a name or alias. * * @throws CommandNotFoundException When command name is incorrect or ambiguous */ public function find(string $name): Command { $this->init(); $aliases = []; foreach ($this->commands as $command) { foreach ($command->getAliases() as $alias) { if (!$this->has($alias)) { $this->commands[$alias] = $command; } } } if ($this->has($name)) { return $this->get($name); } $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*'; $commands = preg_grep('{^'.$expr.'}', $allCommands); if (!$commands) { $commands = preg_grep('{^'.$expr.'}i', $allCommands); } // if no commands matched or we just matched namespaces if (!$commands || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { if (false !== $pos = strrpos($name, ':')) { // check if a namespace exists and contains commands $this->findNamespace(substr($name, 0, $pos)); } $message = sprintf('Command "%s" is not defined.', $name); if ($alternatives = $this->findAlternatives($name, $allCommands)) { // remove hidden commands $alternatives = array_filter($alternatives, fn ($name) => !$this->get($name)->isHidden()); if (1 == \count($alternatives)) { $message .= "\n\nDid you mean this?\n "; } else { $message .= "\n\nDid you mean one of these?\n "; } $message .= implode("\n ", $alternatives); } throw new CommandNotFoundException($message, array_values($alternatives)); } // filter out aliases for commands which are already on the list if (\count($commands) > 1) { $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) { if (!$commandList[$nameOrAlias] instanceof Command) { $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); } $commandName = $commandList[$nameOrAlias]->getName(); $aliases[$nameOrAlias] = $commandName; return $commandName === $nameOrAlias || !\in_array($commandName, $commands, true); })); } if (\count($commands) > 1) { $usableWidth = $this->terminal->getWidth() - 10; $abbrevs = array_values($commands); $maxLen = 0; foreach ($abbrevs as $abbrev) { $maxLen = max(Helper::width($abbrev), $maxLen); } $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) { if ($commandList[$cmd]->isHidden()) { unset($commands[array_search($cmd, $commands)]); return false; } $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; }, array_values($commands)); if (\count($commands) > 1) { $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs)); throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands)); } } $command = $this->get(reset($commands)); if ($command->isHidden()) { throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); } return $command; } /** * Gets the commands (registered in the given namespace if provided). * * The array keys are the full names and the values the command instances. * * @return Command[] */ public function all(?string $namespace = null): array { $this->init(); if (null === $namespace) { if (!$this->commandLoader) { return $this->commands; } $commands = $this->commands; foreach ($this->commandLoader->getNames() as $name) { if (!isset($commands[$name]) && $this->has($name)) { $commands[$name] = $this->get($name); } } return $commands; } $commands = []; foreach ($this->commands as $name => $command) { if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { $commands[$name] = $command; } } if ($this->commandLoader) { foreach ($this->commandLoader->getNames() as $name) { if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { $commands[$name] = $this->get($name); } } } return $commands; } /** * Returns an array of possible abbreviations given a set of names. * * @return string[][] */ public static function getAbbreviations(array $names): array { $abbrevs = []; foreach ($names as $name) { for ($len = \strlen($name); $len > 0; --$len) { $abbrev = substr($name, 0, $len); $abbrevs[$abbrev][] = $name; } } return $abbrevs; } public function renderThrowable(\Throwable $e, OutputInterface $output): void { $output->writeln('', OutputInterface::VERBOSITY_QUIET); $this->doRenderThrowable($e, $output); if (null !== $this->runningCommand) { $output->writeln(sprintf('<info>%s</info>', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void { do { $message = trim($e->getMessage()); if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $class = get_debug_type($e); $title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); $len = Helper::width($title); } else { $len = 0; } if (str_contains($message, "@anonymous\0")) { $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message); } $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX; $lines = []; foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { foreach ($this->splitStringByWidth($line, $width - 4) as $line) { // pre-format lines to get the right string length $lineLength = Helper::width($line) + 4; $lines[] = [$line, $lineLength]; $len = max($lineLength, $len); } } $messages = []; if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); } $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len)); if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); } foreach ($lines as $line) { $messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); } $messages[] = $emptyLine; $messages[] = ''; $output->writeln($messages, OutputInterface::VERBOSITY_QUIET); if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET); // exception related properties $trace = $e->getTrace(); array_unshift($trace, [ 'function' => '', 'file' => $e->getFile() ?: 'n/a', 'line' => $e->getLine() ?: 'n/a', 'args' => [], ]); for ($i = 0, $count = \count($trace); $i < $count; ++$i) { $class = $trace[$i]['class'] ?? ''; $type = $trace[$i]['type'] ?? ''; $function = $trace[$i]['function'] ?? ''; $file = $trace[$i]['file'] ?? 'n/a'; $line = $trace[$i]['line'] ?? 'n/a'; $output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); } $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } while ($e = $e->getPrevious()); } /** * Configures the input and output instances based on the user arguments and options. */ protected function configureIO(InputInterface $input, OutputInterface $output): void { if (true === $input->hasParameterOption(['--ansi'], true)) { $output->setDecorated(true); } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) { $output->setDecorated(false); } if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { $input->setInteractive(false); } switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break; case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break; case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break; case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break; default: $shellVerbosity = 0; break; } if (true === $input->hasParameterOption(['--quiet', '-q'], true)) { $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); $shellVerbosity = -1; } else { if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); $shellVerbosity = 3; } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); $shellVerbosity = 2; } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); $shellVerbosity = 1; } } if (-1 === $shellVerbosity) { $input->setInteractive(false); } if (\function_exists('putenv')) { @putenv('SHELL_VERBOSITY='.$shellVerbosity); } $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; } /** * Runs the current command. * * If an event dispatcher has been attached to the application, * events are also dispatched during the life-cycle of the command. * * @return int 0 if everything went fine, or an error code */ protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int { foreach ($command->getHelperSet() as $helper) { if ($helper instanceof InputAwareInterface) { $helper->setInput($input); } } $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : []; if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) { if (!$this->signalRegistry) { throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); } if (Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); foreach ([\SIGINT, \SIGQUIT, \SIGTERM] as $signal) { $this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode)); } } if ($this->dispatcher) { // We register application signals, so that we can dispatch the event foreach ($this->signalsToDispatchEvent as $signal) { $event = new ConsoleSignalEvent($command, $input, $output, $signal); $this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) { $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL); $exitCode = $event->getExitCode(); // If the command is signalable, we call the handleSignal() method if (\in_array($signal, $commandSignals, true)) { $exitCode = $command->handleSignal($signal, $exitCode); } if (false !== $exitCode) { $event = new ConsoleTerminateEvent($command, $event->getInput(), $event->getOutput(), $exitCode, $signal); $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); exit($event->getExitCode()); } }); } // then we register command signals, but not if already handled after the dispatcher $commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent); } foreach ($commandSignals as $signal) { $this->signalRegistry->register($signal, function (int $signal) use ($command): void { if (false !== $exitCode = $command->handleSignal($signal)) { exit($exitCode); } }); } } if (null === $this->dispatcher) { return $command->run($input, $output); } // bind before the console.command event, so the listeners have access to input options/arguments try { $command->mergeApplicationDefinition(); $input->bind($command->getDefinition()); } catch (ExceptionInterface) { // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition } $event = new ConsoleCommandEvent($command, $input, $output); $e = null; try { $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND); if ($event->commandShouldRun()) { $exitCode = $command->run($input, $output); } else { $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; } } catch (\Throwable $e) { $event = new ConsoleErrorEvent($input, $output, $e, $command); $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); $e = $event->getError(); if (0 === $exitCode = $event->getExitCode()) { $e = null; } } $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); if (null !== $e) { throw $e; } return $event->getExitCode(); } /** * Gets the name of the command based on input. */ protected function getCommandName(InputInterface $input): ?string { return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument(); } /** * Gets the default input definition. */ protected function getDefaultInputDefinition(): InputDefinition { return new InputDefinition([ new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the <info>'.$this->defaultCommand.'</info> command'), new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null), new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), ]); } /** * Gets the default commands that should always be available. * * @return Command[] */ protected function getDefaultCommands(): array { return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()]; } /** * Gets the default helper set with the helpers that should always be available. */ protected function getDefaultHelperSet(): HelperSet { return new HelperSet([ new FormatterHelper(), new DebugFormatterHelper(), new ProcessHelper(), new QuestionHelper(), ]); } /** * Returns abbreviated suggestions in string format. */ private function getAbbreviationSuggestions(array $abbrevs): string { return ' '.implode("\n ", $abbrevs); } /** * Returns the namespace part of the command name. * * This method is not part of public API and should not be used directly. */ public function extractNamespace(string $name, ?int $limit = null): string { $parts = explode(':', $name, -1); return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); } /** * Finds alternative of $name among $collection, * if nothing is found in $collection, try in $abbrevs. * * @return string[] */ private function findAlternatives(string $name, iterable $collection): array { $threshold = 1e3; $alternatives = []; $collectionParts = []; foreach ($collection as $item) { $collectionParts[$item] = explode(':', $item); } foreach (explode(':', $name) as $i => $subname) { foreach ($collectionParts as $collectionName => $parts) { $exists = isset($alternatives[$collectionName]); if (!isset($parts[$i]) && $exists) { $alternatives[$collectionName] += $threshold; continue; } elseif (!isset($parts[$i])) { continue; } $lev = levenshtein($subname, $parts[$i]); if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) { $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; } elseif ($exists) { $alternatives[$collectionName] += $threshold; } } } foreach ($collection as $item) { $lev = levenshtein($name, $item); if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) { $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; } } $alternatives = array_filter($alternatives, fn ($lev) => $lev < 2 * $threshold); ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE); return array_keys($alternatives); } /** * Sets the default Command name. * * @return $this */ public function setDefaultCommand(string $commandName, bool $isSingleCommand = false): static { $this->defaultCommand = explode('|', ltrim($commandName, '|'))[0]; if ($isSingleCommand) { // Ensure the command exist $this->find($commandName); $this->singleCommand = true; } return $this; } /** * @internal */ public function isSingleCommand(): bool { return $this->singleCommand; } private function splitStringByWidth(string $string, int $width): array { // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. // additionally, array_slice() is not enough as some character has doubled width. // we need a function to split string not by character count but by string width if (false === $encoding = mb_detect_encoding($string, null, true)) { return str_split($string, $width); } $utf8String = mb_convert_encoding($string, 'utf8', $encoding); $lines = []; $line = ''; $offset = 0; while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) { $offset += \strlen($m[0]); foreach (preg_split('//u', $m[0]) as $char) { // test if $char could be appended to current line if (mb_strwidth($line.$char, 'utf8') <= $width) { $line .= $char; continue; } // if not, push current line to array and make new line $lines[] = str_pad($line, $width); $line = $char; } } $lines[] = \count($lines) ? str_pad($line, $width) : $line; mb_convert_variables($encoding, 'utf8', $lines); return $lines; } /** * Returns all namespaces of the command name. * * @return string[] */ private function extractAllNamespaces(string $name): array { // -1 as third argument is needed to skip the command short name when exploding $parts = explode(':', $name, -1); $namespaces = []; foreach ($parts as $part) { if (\count($namespaces)) { $namespaces[] = end($namespaces).':'.$part; } else { $namespaces[] = $part; } } return $namespaces; } private function init(): void { if ($this->initialized) { return; } $this->initialized = true; foreach ($this->getDefaultCommands() as $command) { $this->add($command); } } }