An Elegant CLI Library for PHP

Overview

Commando

An Elegant PHP CLI Library

Build Status

Commando is a PHP command line interface library that beautifies and simplifies writing PHP scripts intended for command line use.

Why?

PHP's $argv magic variable and global $_SERVER['argv'] make me cringe, getopt isn't all that much better, and most other PHP CLI libraries are far too bloated for many cases. Commando gets down to business without a ton of overhead, removes the common boilerplate stuff when it comes to handling CLI input, all while providing a clean and readable interface.

Installation

Commando requires that you are running PHP 5.6 or higher.

Commando is PSR-0 compliant and can be installed using Composer. Add nategood/commando to your composer.json

"require": {
    "nategood/commando": "*"
}

If you're new to Composer...

Currently installing via Composer is the only supported option.

Example

Here is an example of a PHP Commando script that gives a decent tour of Commando's features. Let's say it is in a file called hello.php.

<?php

require_once 'vendor/autoload.php';

$hello_cmd = new Commando\Command();

// Define first option
$hello_cmd->option()
    ->require()
    ->describedAs('A person\'s name');

// Define a flag "-t" a.k.a. "--title"
$hello_cmd->option('t')
    ->aka('title')
    ->describedAs('When set, use this title to address the person')
    ->must(function($title) {
        $titles = array('Mister', 'Mr', 'Misses', 'Mrs', 'Miss', 'Ms');
        return in_array($title, $titles);
    })
    ->map(function($title) {
        $titles = array('Mister' => 'Mr', 'Misses' => 'Mrs', 'Miss' => 'Ms');
        if (array_key_exists($title, $titles))
            $title = $titles[$title];
        return "$title. ";
    });

// Define a boolean flag "-c" aka "--capitalize"
$hello_cmd->option('c')
    ->aka('capitalize')
    ->aka('cap')
    ->describedAs('Always capitalize the words in a name')
    ->boolean();

// Define an incremental flag "-e" aka "--educate"
$hello_cmd->option('e')
    ->aka('educate')
    ->map(function($value) {
        $postfix = array('', 'Jr', 'esq', 'PhD');
        return $postfix[$value] === '' ? '' : " {$postfix[$value]}";
    })
    ->count(4);

$name = $hello_cmd['capitalize'] ? ucwords($hello_cmd[0]) : $hello_cmd[0];

echo "Hello {$hello_cmd['title']}$name{$hello_cmd['educate']}!", PHP_EOL;

Running it:

> php hello.php Nate
Hello, Nate!

> php hello.php --capitalize nate
Hello, Nate!

> php hello.php -c -t Mr 'nate good'
Hello, Mr. Nate Good!

> php hello.php -ceet Mr 'nate good'
Hello, Mr. Nate Good esq!

Things to note:

  • Commando implements ArrayAccess so it acts much like an array when you want to retrieve values for it
  • For "anonymous" (i.e. not a named flag) arguments, we access them based on their numeric index
  • We can access option values in an array via a flags name OR its alias
  • We can use closures to perform validation and map operations right as part of our option definition

Baked in Help

Commando has automatic --help support built in. Calling your script with this flag will print out a pretty help page based on your option definitions and Commando settings. If you define an option with the alias of 'help', it will override this built in support.

help screenshot

Error Messaging

By default, Commando will catch Exceptions that occur during the parsing process. Instead, Commando prints a formatted, user-friendly error message to standard error and exits with a code of 1. If you wish to have Commando throw Exceptions in these cases, call the doNotTrapErrors method on your Command instance.

error screenshot

Command Methods

These options work on the "command" level.

useDefaultHelp (bool help)

The default behavior of Commando is to provide a --help option that spits out a useful help page generated off of your option definitions. Disable this feature by calling useDefaultHelp(false)

setHelp (string help)

Text to prepend to the help page. Use this to describe the command at a high level and maybe some examples usages of the command.

printHelp()

Print the default help for the command. Useful if you want to output help if no arguments are passed.

beepOnError (bool beep=true)

When an error occurs, print character to make the terminal "beep".

getOptions

Return an array of Options for each options provided to the command.

getFlags

Return an array of Options for only the flags provided to the command.

getArguments

Return an array of Options for only the arguments provided to the command. The order of the array is the same as the order of the arguments.

getFlagValues

Return associative array of values for arguments provided to the command. E.g. array('f' => 'value1').

getArgumentValues

Return array of values for arguments provided to the command. E.g. array('value1', 'value2').

Command Option Definition Methods

These options work on the "option" level, even though they are chained to a Command instance

option (mixed $name = null)

Aliases: o

Define a new option. When name is set, the option will be a named "flag" option. Can be a short form option (e.g. f for option -f) or long form (e.g. foo for option --foo). When no name is defined, the option is an anonymous argument and is referenced in the future by its position.

flag (string $name)

Same as option except that it can only be used to define "flag" type options (a.k.a. those options that must be specified with a -flag on the command line).

argument ()

Same as option except that it can only be used to define "argument" type options (a.k.a those options that are specified WITHOUT a -flag on the command line).

alias (string $alias)

Aliases: a, aka

Add an alias for a named option. This method can be called multiple times to add multiple aliases.

description (string $description)

Aliases: d, describe, describedAs

Text to describe this option. This text will be used to build the "help" page and as such, it is end user facing.

require (bool $require)

Aliases: r, required

Require that this flag is specified

needs (string|array $options)

Aliases: none

Require that other $options be set for this option to be used.

must (Closure $rule)

Aliases: N/A

Define a rule to validate input against. Takes function that accepts a string $value and returns a boolean as to whether or not $value is valid.

map (Closure $map)

Aliases: cast, castTo

Perform a map operation on the value for this option. Takes function that accepts a string $value and return mixed (you can map to whatever you wish).

reduce (Closure $reducer [, mixed $seed])

Aliases: list, each, every

Execute an accumulator/reducer function on every instance of the option in the command. Takes an accumulator function, and returns mixed (you can return any value). If you also supply a map for the option the map will execute on every value before it is passed to the accumulator function. If $seed value is supplied, this will be used as the default value.

Signature: function(mixed $accumulated, mixed $value) : mixed

  • $accumulated: null|Option::default|mixed (the last value returned from the function, the option default value, or null.)
  • $value: mixed (the value that comes after the option. if map is supplied, the value returned from the map function.)
  • return: mixed (anything you want. The last value returned becomes the value of the Option after parsing.)

referToAs (string $name)

Aliases: title, referredToAs

Add a name to refer to an argument option by. Makes the help docs a little cleaner for anonymous "argument" options.

boolean ()

Aliases: N/A

Specifices that the flag is a boolean type flag.

increment (int $max)

Aliases: i, count, repeats, repeatable

Specifies that the flag is a counter type flag. The value of the flag will be incremented up to the value of $max for each time the flag is used in the command. Options that are set to increment or boolean types can be grouped together.

default (mixed $defaultValue)

Aliases: defaultsTo

If the value is not specified, default to $defaultValue.

In the case of boolean() type flags, when the flag is present, the value of this option the negation of $defaultValue. That is to say, if you have a flag -b with a default of true, when -b is present as a command line flag, the value of the option will be false.

file ()

Aliases: expectsFile

The value specified for this option must be a valid file path. When used relative paths will be converted into fully quantify file paths and globbing is also optionally supported. See the file.php example.

boolean ()

Aliases: N/A

Specifices that the flag is a boolean type flag.

default (mixed $defaultValue)

Aliases: defaultsTo

If the value is not specified, default to $defaultValue.

In the case of boolean() type flags, when the flag is present, the value of this option the negation of $defaultValue. That is to say, if you have a flag -b with a default of true, when -b is present as a command line flag, the value of the option will be false.

file ()

Aliases: expectsFile

The value specified for this option must be a valid file path. When used relative paths will be converted into fully quatified file paths and globbing is also optionally supported. See the file.php example.

Contributing

Commando highly encourages sending in pull requests. When submitting a pull request please:

  • All pull requests should target the dev branch (not master)
  • Make sure your code follows the coding standards laid out in PSR-1 and PSR-2
  • Make sure you add appropriate test coverage for your changes
  • Run all unit tests in the test directory via phpunit ./tests
  • Include commenting where appropriate and add a descriptive pull request message

Inspiration

Released under MIT license.

Change Log

v0.4.0

  • Dropping support for 5.4 and 5.5, bumping minor version number
  • PR #93 FEATURE Add reducer option
  • PR #95 FIX Remove tput call on Windows OS
  • PR #101 FIX Only evaluate 'needs' constraints of an option if that option is actually used
  • PR #76 FIX Fix non-empty getArgumentValues array when using anonymous args

v0.3.0

  • Dropped PHP 5.3

v0.2.9

  • PR #63 FEATURE incremental flags
  • PR #60 MINOR getDescription method

v0.2.8

  • Bug fix for #34

v0.2.7

  • getOptions added (along with some better documentation)

v0.2.6

  • Adds support for "needs" to define dependencies between options (thanks @enygma) PR #31
  • Fixes issue with long-argument-names Issue #30

v0.2.5

  • Fixed up default values for boolean options, automatically default boolean options to false (unlikely, but potentially breaking change) PR #19

v0.2.4

  • Added ability to define default values for options

v0.2.3

  • Improved Help Formatting PR #12

v0.2.2

  • Bug fix for printing double help PR #10

v0.2.1

  • Adds support for requiring options to be valid file paths or globs
  • Returns a fully qualified file path name (e.g. converts relative paths)
  • Returns an array of file paths in the case of globbing
  • See the file.php example in the examples directory

v0.2.0

The primary goal of this update was to better delineate between flag options and argument options. In Commando, flags are options that we define that require a name when they are being specified on the command line. Arguments are options that are not named in this way. In the example below, '-f' and '--long' are described as "flags" type options in Commando terms with the values 'value1' and 'value2' respectively, whereas value3, value4, and value5 are described as "argument" type options.

php command.php -f value1 --long value2 value3 value4 value5
  • Added Command::getArguments() to return an array of Option that are of the "argument" type (see argumentsVsFlags.php example)
  • Added Command::getFlags() to return an array of Option that are of the "flag" type (see argumentsVsFlags.php example)
  • Added Command::getArgumentValues() to return an array of all the values for "arguments"
  • Added Command::getFlagValues() to return an array of all values for "flags"
  • Command now implements Iterator interface and will iterator over all options, starting with arguments and continuing with flags in alphabetical order
  • Can now define options with Command::flag($name) and Command::argument(), in addition to Command::option($name)
  • Added ability to add a "title" to refer to arguments by, making the help docs a little cleaner (run help.php example)
  • Cleaned up the generated help docs
  • Bug fix for additional colorized red line when an error is displayed

v0.1.4

  • Bug fix for options values with multiple words

v0.1.3

  • Beep support added to Terminal
  • Commando::beepOnError() added

v0.1.2

  • Terminal updated to use tput correctly
Comments
  • Add requires

    Add requires

    Adding the concept of "required options". Adds dependency between options such that:

    $cmd = new Command(); $cmd->option('test') ->requires('other');

    So that if "--test" is defined, "--other" must be also otherwise an exception is thrown. Options can also have multiple dependencies:

    $cmd = new Command(); $cmd->option('test') ->requires(array('other','option'));

    Where both "--other" and "--option" must be defined. Unit tests are included.

    opened by enygma 12
  • Value of option argument not getting set

    Value of option argument not getting set

    Value isn't getting set when passing a argument to a option

    use Commando\Command;
    
    $tokens = array('status');
    $cmd = new Command($tokens);
    $cmd->setHelp('This tool is for managing status from the cli. It takes no arguments just options that take arguments')
        ->option()->describe('status take a optional single action argument. e.g. status {create|view|update}')
        ->option('m')->aka('message')->describe('This is message you wish to use with create or update. wrap in double quotes or vim will be used');
    var_dump($cmd->getOption('m'));
    var_dump($cmd['message']);exit;
    
    08:43:49 baldr:[~/cvs/status-cli/bin] $ php status.php create -m 'this is a test'
    object(Commando\Option)#10 (9) {
      ["name":"Commando\Option":private]=>
      string(1) "m"
      ["aliases":"Commando\Option":private]=>
      array(1) {
        [0]=>
        string(7) "message"
      }
      ["value":"Commando\Option":private]=>
      NULL
      ["description":"Commando\Option":private]=>
      string(96) "This is message you wish to use with create or update. wrap in double quotes or vim will be used"
      ["required":"Commando\Option":private]=>
      bool(false)
      ["boolean":"Commando\Option":private]=>
      bool(false)
      ["type":"Commando\Option":private]=>
      int(1)
      ["rule":"Commando\Option":private]=>
      NULL
      ["map":"Commando\Option":private]=>
      NULL
    }
    NULL
    
    opened by moos3 9
  • Multiple valued option - how to?

    Multiple valued option - how to?

    I want to declare an option which could be invoked several times, e.g.:

    --option value1 --option value2 --option value3
    

    This is similar to what tar have for files exclusion, .e.g

    $ tar --exclude='./folder' --exclude='./upload/folder2' -zcvf /backup/filename.tgz .
    

    I cannot find a way how to do this, or I'm missing something?

    opened by OnkelTem 7
  • Implementation of repeatable options

    Implementation of repeatable options

    Hi,

    I ended up implementing this for a project I'm working on. I called it magnitude, but could be renamed repeatable or something else. If there's any feedback, let me know and I can make edits.

    The one "hack" is that options that have multiple letters that repeat are assumed to be "magnitude." I just didn't see any better way to do it.

    Tests pass, but I have some more testing to do and see what happens.

    Tested on PHP 5.4.30

    opened by ghost 6
  • option->map() is not called on default value

    option->map() is not called on default value

    for example, if I call $command->option("foo")->default() and then followed by another map() call, the closure passed to map() is not called on the default value. Is this intentional (design?) or this is a bug?

    let me know if you need further information

    opened by Jeffrey04 6
  • Avoid double help

    Avoid double help

    This assures the parse method is called only once.

    When the script is short-circuited on default help, it exists without mark the command as parsed, showing the helo message twice.

    All tests passed.

    opened by felds 5
  • --help doesn't work

    --help doesn't work

    When executing the examples/help.php example, if the last line ($cmd->printHelp();) is removed, the help message doesn't work, even if the script is called with the --help flag.

    opened by felds 5
  • tputs is sometimes tput

    tputs is sometimes tput

    I just checked on two systems: Ubuntu 10.04.4 LTS and Mac OS X 10.7.4 Lion: tputs is not installed, but instead uses tput. I think this might be a change in the ncurses library? Use of tputs

    opened by zackdouglas 5
  • Use titles for anonymous arguments where provided, doc fix

    Use titles for anonymous arguments where provided, doc fix

    Instead of "Required argument 0" when leaving off required anonymous arguments, this will say "Required argument name" when ->title('name') is added to the option builder chain.

    Test added.

    README.md updated to remove duplicate file listing.

    opened by tolidano 4
  • Updating PHPDocs for easier use in PhpStorm

    Updating PHPDocs for easier use in PhpStorm

    Using Command in PhpStorm always gives me many errors as Command uses __call for various methods that PhpStorm (or other IDE's as I expect) can't resolve.

    I've added the methodes using phpdocs's standards and various other phpdoc errors were fixed as well.

    opened by michielroding 4
  • Needs() method looking at the wrong thing?

    Needs() method looking at the wrong thing?

    Either the needs method isn't working, or my understanding of what it's supposed to do isn't working.

    Say you've got a script with two options: -t and -e. If you use -t, you must include -e with a value. According to the documentation, needs('e') on the -t option would require the -e option to be set. However, that doesn't seem to be the case.

    For example: <?php require_once 'vendor/autoload.php';

    $cmd = new Commando\Command();
    $cmd->option('e');
    $cmd->option('t')
        ->boolean()
        ->needs('e');
    
    var_dump($cmd['e']);
    var_dump($cmd['t']);
    

    It appears that when it's checking to see if the -e option is set, it actually is just checking to see if there is a -e option defined (src/Commando/Option.php: 305) not that it has been passed in by the user.

    I'm willing to fix the issue if this isn't how the method is intended to behave.

    opened by omnicolor 4
  • PHP 8.1.1 - PHP Deprecated Warning ReturnTypeWillChange

    PHP 8.1.1 - PHP Deprecated Warning ReturnTypeWillChange

    $ php composer.phar info | grep commando nategood/commando 0.4.0 PHP CLI Commando Style

    $ php -v PHP 8.1.1 (cli) (built: Dec 17 2021 23:49:52) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.1, Copyright (c) Zend Technologies with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies


    When running one of my scripts I'm getting the deprecation messages below when executing the following commands:

    $cmds = new Commando\Command(); $cmds->option('c')->aka('country')->describedAs('2 or 3 letter country code')->required(); $cmds->option('p')->aka('provinces')->describedAs('List provinces')->boolean();

    PHP Deprecated: Return type of Commando\Command::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 842

    PHP Deprecated: Return type of Commando\Command::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 853

    PHP Deprecated: Return type of Commando\Command::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 869

    PHP Deprecated: Return type of Commando\Command::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 878

    PHP Deprecated: Return type of Commando\Command::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 895

    PHP Deprecated: Return type of Commando\Command::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 912

    PHP Deprecated: Return type of Commando\Command::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 904

    PHP Deprecated: Return type of Commando\Command::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 921

    PHP Deprecated: Return type of Commando\Command::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in ***/vendor/nategood/commando/src/Commando/Command.php on line 886

    opened by stevenjcarr 0
  • bypass exit on default help

    bypass exit on default help

    By default when the help screen was printed the script is ended by exit(). There are cases were this behavior is undesired. The change allows to bypass this, the default behavior is unaffected.

    Additionally I fixed useDefaultHelp() to match into the fluid interface.

    opened by djaenecke 0
  • Change Iterator to return keys based on option names.

    Change Iterator to return keys based on option names.

    This is expected behaviour because ArrayAccess works in same manner. As you can use $cmd['optionName'] to get value, you can do same with $cmdArray = iterator_to_array($cmd); $cmdArray['optionName'].

    opened by sharkodlak 0
  • Add an option to throw an exception or return instead of calling exit()

    Add an option to throw an exception or return instead of calling exit()

    Calling exit aborts the entire application making it difficult work around in some situations, such as during unit tests or when the outcome of an invocation should be logged.

    An option to throw an exception instead of calling exit would be great!

    Relevant line: https://github.com/nategood/commando/blob/7ae153aa211c0686f139062856642e33bf8a86e0/src/Commando/Command.php#L460

    opened by d0x2f 1
Owner
Nate Good
CTO
Nate Good
Patrol is an elegant command-line tool that keeps your PHP Project's dependencies in check.

Patrol is an elegant command-line tool that keeps your PHP Project's dependencies in check. Installation / Usage Requires PHP 8.0+ First, install Patr

Nuno Maduro 237 Nov 14, 2022
Library for creating CLI commands or applications

Console Motivation: this library purpose is to provide a lighter and more robust API for console commands and/or applications to symfony/console. It c

Théo FIDRY 16 Dec 28, 2022
🖥 Build beautiful PHP CLI menus. Simple yet Powerful. Expressive DSL.

Contents Minimum Requirements Installation Upgrading Usage Quick Setup Examples API Appearance Menu Title Colour Width Padding Margin Borders Exit But

PHP School 1.9k Dec 28, 2022
Cilex a lightweight framework for creating PHP CLI scripts inspired by Silex

Cilex, a simple Command Line Interface framework Cilex is a simple command line application framework to develop simple tools based on Symfony2 compon

null 624 Dec 6, 2022
PHP Version Manager for the CLI on Windows

This package has a much more niche use case than nvm does. When developing on Windows and using the integrated terminal, it's quite difficult to get those terminals to actually listen to PATH changes.

Harry Bayliss 49 Dec 19, 2022
PHP CLI tool which allows publishing zipped MODX extra to modstore.pro marketplace

MODX Extra Publisher PHP CLI tool which allows publishing zipped MODX extra to modstore.pro marketplace. Installation global? local? To install packag

Ivan Klimchuk 3 Aug 6, 2021
PHP CLI project to get an appointment from https://vacunacovid.catsalut.gencat.ca

covid_vaccine_bcn PHP CLI project to get an appointment from https://citavacunacovid19.catsalut.gencat.cat/Vacunacio_Covid/Vacunacio/VacunacioCovidRes

Gabriel Noé González 3 Jul 27, 2021
PHP CLI to add latest release notes to a CHANGELOG

changelog-updater A PHP CLI to update a CHANGELOG following the "Keep a Changelog" format with the latest release notes. Want to automate the process

Stefan Zweifel 15 Sep 21, 2022
unofficial cli built using php which can be used to upload and download files from anonfiles.com

Anonfiles CLI Table of Contents Introduction Features Screenshots Installation Contributing License Introduction Anon Files CLI can upload and downloa

Albin Varghese 8 Nov 21, 2022
A handy set of Stringable mixins for CLI text.

Laravel Colorize A mixin for Laravel's Stringable to easily apply colors and styles to CLI text. Installation You can install the package via Composer

James Brooks 47 Oct 30, 2022
WP-CLI Trait Package Command

WP-CLI Trait Package Command Generate plugin or php model files e.g. post-type or taxonomy for WP-Trait Package in Develop WordPress Plugin. Installat

Mehrshad Darzi 2 Dec 17, 2021
A CLI program that helps you check your endpoints by requesting the given servers and send a report message in any supported channel like Telegram

API Monitor A CLI program that help you check your endpoints by requesting the given servers and send a report message in any supported channel ( Tele

Hussein Feras 51 Aug 21, 2022
Termage provides a fluent and incredibly powerful object-oriented interface for customizing CLI output text color, background, formatting, theming and more.

Termage provides a fluent and incredibly powerful object-oriented interface for customizing CLI output text color, background, formatting, theming and

TERMAGE 75 Dec 20, 2022
A CLI starter pack for developing a package with Laravel 5

Laravel PackMe Laravel PackMe is a project starter pack which combine all basic stuff (src, tests) in order to develop a package for Laravel 5.*. It t

Pierre Tondereau 63 Dec 29, 2021
PHPFusion CLI

PHPFusion CLI Installation Source Add path\to\PF-CLI\bin to your system PATH .phar file Download pf.phar Check the Phar file to verify that it's worki

PF Projects 0 Mar 20, 2022
Drupal.org Git CLI

doGit Drupal.org + Git CLI application. doGit assists in making the transition to merge requests, and general Git operations, easier for Drupal develo

dpi 16 Dec 15, 2022
A Magento 2 module that adds a CLI bin/magento cms:dump to dump all CMS pages and CMS blocks to a folder var/cms-output.

A Magento 2 module that adds a CLI bin/magento cms:dump to dump all CMS pages and CMS blocks to a folder var/cms-output.

Yireo 16 Dec 16, 2022
Host Onion services in dark web using Heroku CLI

Tor Onion Service On Heroku Host Tor v3 Hidden Service in dark web using heroku Try my another repository built with php https://github.com/sumithemma

Emmadi Sumith Kumar 34 Dec 13, 2022