Introducing minicli: a microframework for CLI-centric PHP applications
Tags: showdev, php, cli
Published: 2020-03-02 11:59:05

---
title: Introducing minicli: a microframework for CLI-centric PHP applications
published: true
description: In the previous posts of the Building Minicli series, we've seen how to bootstrap a dependency-free microframework on top of vanilla PHP. This post will give you an overview of how to use the most updated version of Minicli to create a CLI application in PHP.
series: building-minicli
tags: showdev, php, cli
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/cww20brm0vqazrbam2yv.png
---

In the previous posts of the [Building Minicli](https://dev.to/erikaheidi/bootstrapping-a-cli-php-application-in-vanilla-php-4ee) series, we demonstrated the steps to bootstrap a dependency-free microframework for CLI-only applications in PHP. 

Minicli was created as an educational experiment and also as a lightweight base that I could reuse for my personal projects. I like to think of it as the most basic unit I was able to put together, on top of which I could build my toys.

It's been months since I shared my latest post on this series, and I was reluctant to share what I've been working on because it always feels like an incomplete work. But will it ever be complete (or feel like so)? Probably not. Minicli is open source since day 0, and although I never intended to turn it into a mainstream project, I also think it can help people who are interested in building simple things in the command line without the overkill of dozens of external requirements.

So I'd like to officially introduce you to [Minicli](https://github.com/minicli/minicli), a *highly experimental* dependency-free microframework for CLI-centric PHP apps. 

While I don't advocate for reinventing all the wheels in an application, I believe there should be a starting point that doesn't require 10+ different libraries for basic parsing and routing of commands. From there, you should be able to consciously choose what you'll be depending on, in terms of external libraries. Minicli is what I came up with in order to solve this situation.

### What I've built with Minicli so far:

[Dolphin](https://github.com/do-community/dolphin), a command-line tool for managing DigitalOcean droplets from the command line.
![Dolphin screenshot](https://camo.githubusercontent.com/eb3730afadf3df143af400fc1240afcbf7084452/68747470733a2f2f6865696469736c61622e616d73332e63646e2e6469676974616c6f6365616e7370616365732e636f6d2f646f6c7068696e2f73637265656e73686f742e706e67)

[My website](https://eheidi.dev), which is a static-content CMS [pulling from my DEV posts](https://dev.to/erikaheidi/how-to-create-a-dev-to-api-wrapper-in-php-to-fetch-your-latest-posts-1fei). I'm open sourcing this as a separate project called [Librarian](https://github.com/minicli/librarian) (WIP).

![Screenshot of eheidi.dev](https://dev-to-uploads.s3.amazonaws.com/i/hg6mqo4882rrjl5kiuct.png)

In this post, you will learn how to create a simple CLI application in PHP using Minicli.

## Creating a Project

You'll need `php-cli` and [Composer](https://getcomposer.org/) to get started.

Create a new project with:

```shell
composer create-project --prefer-dist minicli/application myapp
```

Once the installation is finished, you can run `minicli` with:

```shell
cd myapp
./minicli
```

This will show you the default app signature.

The `help` command that comes with minicli, defined in `app/Command/Help/DefaultController.php`, auto-generates a tree of available commands:

```shell
./minicli help
```

```
Available Commands

help
└──test

```

The `help test` command, defined in `app/Command/Help/TestController.php`, shows an echo test of parameters:

```
./minicli help test user=erika name=value
```

```
Hello, erika!

Array
(
    [user] => erika
    [name] => value
)
```

## Creating your First Command

The simplest way to create a command is to edit the `minicli` script and define a new command as an anonymous function within the Application via `registerCommand`:

```php
#!/usr/bin/php
<?php

if (php_sapi_name() !== 'cli') {
    exit;
}

require __DIR__ . '/vendor/autoload.php';

use Minicli\App;
use Minicli\Command\CommandCall;

$app = new App();
$app->setSignature('./minicli mycommand');

$app->registerCommand('mycommand', function(CommandCall $input) {
    echo "My Command!";

    var_dump($input);
});

$app->runCommand($argv);
```

You could then execute the new command with:

```shell
./minicli mycommand
```

## Using Command Controllers

To organize your commands into controllers, you'll need to use [Command Namespaces](https://dev.to/erikaheidi/building-minicli-autoloading-command-namespaces-3ljm). 

Let's say you want to create a command named  `hello`. You should start by creating a new directory under the `app/Commands` folder:

```shell
mkdir app/Commands/Hello
```

Now `Hello` is your Command Namespace. Inside that directory, you'll need to create at least one Command Controller. You can start with the `DefaultController`, which will be called by default when no subcommand is provided.

This is how this `DefaultController` class could look like:

```php
<?php

namespace App\Command\Hello;

use Minicli\Command\CommandController;

class DefaultController extends CommandController
{
    public function handle()
    {       
        $this->getPrinter()->display("Hello World!");
    }
}
```

This command would be available as:

```shell
./minicli hello
```

Becase a subcommand was not provided, it is inferred that you want to execute the **default** command. This command can also be invoked as:

```shell
./minicli hello default
```

Any other Command Controller placed inside the `Hello` namespace will be available in a similar way. For instance, let's say you want to create a new subcommand like `hello caps`.

You would then create a new Command Controller named `CapsController`:

```php
<?php

namespace App\Command\Hello;

use Minicli\Command\CommandController;

class CapsController extends CommandController
{
    public function handle()
    {       
        $this->getPrinter()->display("HELLO WORLD!");
    }
}
```

And this new command would be available as:

```shell
./minicli hello caps
```

## Working with Parameters

Minicli uses a few conventions for command call arguments:

* Args / Arguments: Parsed arguments - anything that comes from $argv that is not a `key=value` and not a `--flag`.
* Params / Parameters: Key-value pairs such as `user=erika`
* Flags: single arguments prefixed with `--` such as `--update`

The parent `CommandController` class exposes a few handy methods to work with the command call parameters.
For instance, let's say you want to update the previous `hello` command to use an optional parameter to tell the name of the person that will be greeted.

```php
<?php

namespace App\Command\Hello;

use Minicli\Command\CommandController;
use Minicli\Input;

class HelloController extends CommandController
{
    public function handle()
    {       
        $name = $this->hasParam('user') ? $this->getParam('user') : 'World';
        $this->getPrinter()->display(sprintf("Hello, %s!", $name));
    }
}
```

Now, to use the custom version of the command, you'll need to run:

```shell
./minicli hello user=erika 
```

And you'll get the output:

```shell
Hello, erika!
```

### `CommandCall` Class Methods
* `hasParam(string $key) : bool` - Returns true if a parameter exists.
* `getParam(string $key) : string` - Returns a parameter, or null if its non existent.
* `hasFlag(string $key) : bool` - Returns whether or not a flag was passed along in the command call.

## Printing Output

The `CliPrinter` class has shortcut methods to print messages with various colors and styles. 
It comes with two bundled *themes*: `regular` and `unicorn`. This is set up within the App bootstrap config array, and by default it's configured to use the `regular` theme.

```php
    public function handle()
    {       
        $this->getPrinter()->info("Starting Minicli...");
        if (!$this->hasParam('message')) {
            $this->getPrinter()->error("Error: you must provide a message.");
            exit;
        }
        
        $this->getPrinter()->success($this->getParam('message'));
    }
```
### `CliPrinter` Class Methods

* `display(string $message) : void` - Displays a message wrapped in new lines.
* `error(string $message) : void` - Displays an error message wrapped in new lines, using the current theme colors.
* `success(string $message) : void` - Displays a success message wrapped in new lines, using the current theme colors.
* `info(string $message) : void` - Displays an info message wrapped in new lines, using the current theme colors.
* `newline() : void` - Prints a new line.
* `format(string $message, string $style="default") : string` - Returns a formatted string with the desired style.
* `out(string $message) : void` - Prints a message.


## Wrapping Up

Minicli is a work in progress, but you can already use it as a minimalist base on top of which you can build fun toy projects and/or helpful command line tools, like [Dolphin](https://github.com/do-community/dolphin). 

Here's a few ideas I'd like to build with Minicli but haven't had the time so far (and I definitely wouldn't mind if anyone build these):

* a text-based rpg game
* a Twitter bot
* a tool for finding your Twitter mutuals
* a cli-based quizz game

If you'd like to give Minicli a try, check the [documentation](https://minicliphp.readthedocs.io/en/latest/) for more details and don't hesitate to leave a comment if you have any questions :)