PHPackages                             knivey/cmdr - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. knivey/cmdr

ActiveLibrary[CLI &amp; Console](/categories/cli)

knivey/cmdr
===========

Command router for IRC bots

v4.0.0(3y ago)298MITPHPPHP &gt;=8.1CI failing

Since Apr 7Pushed 1y ago1 watchersCompare

[ Source](https://github.com/knivey/Cmdr)[ Packagist](https://packagist.org/packages/knivey/cmdr)[ RSS](/packages/knivey-cmdr/feed)WikiDiscussions master Synced 6d ago

READMEChangelog (5)Dependencies (2)Versions (7)Used By (0)

[![Continuous Integration](https://github.com/knivey/Cmdr/actions/workflows/ci.yml/badge.svg)](https://github.com/knivey/Cmdr/actions/workflows/ci.yml)

Cmdr
====

[](#cmdr)

`knivey/cmdr` is command router designed for use on chatbots.

### Requirements

[](#requirements)

- PHP &gt;= 8.2

### Features

[](#features)

- Arguments parsed/validated and easily accessed according to a syntax defined for command
- Uses function attributes to register functions for commands
- Can set up a wrapper function with `CallWrap` attribute (helpful for using something like `\Amp\asyncCall`)

Installation
------------

[](#installation)

```
composer require knivey/cmdr
```

Documentation &amp; Examples
----------------------------

[](#documentation--examples)

Set up the command router object and find commands

```
use knivey\cmdr;
$router = new cmdr\Cmdr();

//All command functions will have Request as the last argument
//command names must not contain a # or space
#[cmdr\attributes\Cmd("example", "altname")] //define as many cmds for this function as you want
#[cmdr\attributes\PrivCmd("example", "altname")] //private message command
#[cmdr\attributes\Syntax(" [optional]")]
#[cmdr\attributes\Options("--option", "--anotheroption")] //Options will have No description
#[cmdr\attributes\Option("--optionb", "description of option")]
#[cmdr\attributes\Option("--optionc", "description of option")]
#[cmdr\attributes\Desc("description of cmd")]
function exampleFunc($additonal, $arguments, cmdr\Args $request) {
    echo $request->args["required"];
    if(isset($request->args["optional"]))
        echo $request->args["optional"];
    if($request->optEnabled('--option'))
        echo "--option was used";
}

#[cmdr\attributes\Cmd("example2")]
#[cmdr\attributes\Desc("Second example command")]
#[cmdr\attributes\Syntax("[args]...")] //the ... means it will eat the rest of the arguments "blah blah etc.."
#[cmdr\attributes\Option("--option", "enable some option")]
function exampleFunc2($additonal, $arguments, cmdr\Args $request) {
    // example2 blah blah --option=value
    // example2 blah --option=value blah (args will just have "blah blah")
    // if just --option then its val is true
    if($val = $request->getOpt('--option')) {
        echo "--option was set to $val";
    }
}

#[cmdr\attributes\PrivCmd("example2")]
#[cmdr\attributes\Desc("Command for private message example command")]
function exampleFunc2($additonal, $arguments, cmdr\Args $request) {
}

//Do this AFTER all functions you want to load are defined
$router->loadFuncs();
//If you have objects you use
$router->loadMethods($object);

// This will return what the command function returns
// Exceptions will be thrown if the args given don't pass the syntax
// Exception also thrown if the command doesn't exist
$router->call('example', 'arguments given to cmd', "additional", "arguments");

//simple example of a fictional IRC chatbot using triggers (!cmd)
function handleMsg(IRCuser $user, string $target, string $text) {
    global $router;
    if(isChan($target)) {
        $trigger = "!";
        if(substr($text, 0, 1) != $trigger)
            return;
        $text = substr($text, 1);
    }
    $text = explode(' ', $text);
    $cmd = array_shift($text);
    $text = implode(' ', $text);
    if(isChan($target)) {
        if($router->cmdExists($cmd)) {
            try {
                return $router->call($cmd, $text, $user);
            } catch (Exception $e) {
                //Exception may be bad arguments etc..., tell the user
                notice($nick, $e->getMessage());
            }
        }
    } else {
        if($router->cmdExistsPriv($cmd)) {
            try {
                return $router->callPriv($cmd, $text, $user);
            } catch (Exception $e) {
                notice($nick, $e->getMessage());
            }
        }
    }
}
```

**Note that command names cannot contain a # or a space in them.**

To load public methods from a class object use `loadMethods($obj)`

### Argument Syntax Rules

[](#argument-syntax-rules)

- `` is a required arg
- `...` required multiword arg, must be last in list
- `[arg]` is an optional arg, all optionals must be at the end and no multiword arguments preceed them
- `[arg]...` optional multiword arg, must be last in list
- The arg name must not contain `[]`

### CallWrap

[](#callwrap)

```
use knivey\cmdr;

#[cmdr\attributes\Cmd("example")]
//The callable for the cmd function is given to wrapper
#[cmdr\attributes\CallWrap("\Amp\asyncCall")]
function example(cmdr\Request $request) {
}

#[cmdr\attributes\Cmd("example2")]
//You can give additional arguments to the wrapper
//pre is before the callable post is after
#[cmdr\attributes\CallWrap("someWrapperFunc", preArgs: [1,2,3], postArgs: ['stuff'])]
function example2(cmdr\Request $request) {
}

function someWrapperFunc($one, $two, $three, callable $func, $stuff, cmdr\Args $request) {
    return $func($request);
}
```

Argument validators (WIP)
-------------------------

[](#argument-validators-wip)

You can specify some simple validations in the argument DSL.

Validators can also filter/normalize inputs, for example bool can take "yes" and set that as true.

### Syntax

[](#syntax)

```

```

- Arguments can only have one validation applied to them.
- The Validate class provides some default validators and custom validators can be registsered.
- Validator args must not have spaces, `` would be an error.
- Validator/filter args should not be named `val`, that'n conventionally used as the first function argument.

### Built in validators

[](#built-in-validators)

#### uint, int, number

[](#uint-int-number)

These all do what you would expect, check for types of numeric values.

They take two optional arguments `min` and `max`.

They will cast to the appropriate PHP type, for int or uint that is `int` numeric could be `int` or `float`.

Example:

```

```

```

```

#### bool

[](#bool)

Can take "yes", "on", "true", "no", "off", "false" and returns a PHP `bool`

#### options/choices

[](#optionschoices)

This one lets you limit the argument to a few choices. To use an optional you only need to put options in parentheses with a | to separate choices.

Example:

```

 functions the same as above

```

### Custom validators and filters

[](#custom-validators-and-filters)

You can set your own custom validators:

```
Validate::setValidator("even", function(string $val) {
    if (!Validate::int($val))
        return false;
    return $val%2==0;
});
```

```

```

You can set filters, these are what normalize values for you.

```
Validate::setValidator("upper", fn ($val) => true);
Validate::setFilter("upper", function($val, $moo=false) {
    if($moo) return strtoupper($val);
    return $val;
});
```

```

 //invalid, validator arguments must match function argument names

```

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance34

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~139 days

Recently: every ~168 days

Total

6

Last Release

1170d ago

Major Versions

1.0 → v2.0.02021-05-01

v2.2.0 → v3.0.02021-08-16

v3.0.0 → v4.0.02023-03-04

PHP version history (3 changes)1.0PHP &gt;8.0

v2.0.0PHP &gt;=8.0

v4.0.0PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/46a26dcd906261aed960a49fa54d83fc74046ced17d9bce0f68f751236f706de?d=identicon)[knivey](/maintainers/knivey)

---

Top Contributors

[![knivey](https://avatars.githubusercontent.com/u/4189618?v=4)](https://github.com/knivey "knivey (32 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/knivey-cmdr/health.svg)

```
[![Health](https://phpackages.com/badges/knivey-cmdr/health.svg)](https://phpackages.com/packages/knivey-cmdr)
```

###  Alternatives

[wp-cli/wp-cli

WP-CLI framework

5.0k17.2M320](/packages/wp-cli-wp-cli)[consolidation/annotated-command

Initialize Symfony Console commands from annotated command class methods.

22569.8M19](/packages/consolidation-annotated-command)[chi-teck/drupal-code-generator

Drupal code generator

26947.8M5](/packages/chi-teck-drupal-code-generator)[seld/cli-prompt

Allows you to prompt for user input on the command line, and optionally hide the characters they type

24725.8M17](/packages/seld-cli-prompt)[illuminate/console

The Illuminate Console package.

12944.1M5.1k](/packages/illuminate-console)[php-tui/php-tui

Comprehensive TUI library heavily influenced by Ratatui

589747.0k6](/packages/php-tui-php-tui)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
