Bootstrapping a CLI PHP application in Vanilla PHP
Tags: showdev, php, cli, beginners
Published: 2019-09-20 14:39:29

---
title: Bootstrapping a CLI PHP application in Vanilla PHP
published: true
description: 
series: building-minicli
tags: showdev, php, cli, beginners
cover_image: https://thepracticaldev.s3.amazonaws.com/i/3w8eirpe8na1463z079q.jpg
---

## Introduction

PHP is well known for its popularity with web applications and CMSs, but what many people don't know is that PHP is also a great language for building command line applications that don't require a web server. Its ease of use and familiar syntax make it a low-barrier language for complimentary tools and little applications that might communicate with APIs or execute scheduled tasks via Crontab, for instance, but without the need to be exposed to external users.

Certainly, you can build a one-file PHP script to attend your needs and that might work reasonably well for tiny things; but that makes it very hard for maintaining, extending or reusing that code in the future. The same principles of web development can be applied when building command-line applications, except that we are not working with a front end anymore - yay! Additionally, the application is not accessible to outside users, which adds security and creates more room for experimentation. 

I'm a bit sick of web applications and the complexity that is built around front-end lately, so toying around with PHP in the command line was very refreshing to me personally. In this post/series, we'll build together a minimalist / dependency-free CLI AppKit (think a tiny framework) - **minicli** - that can be used as base for your experimental CLI apps in PHP.

_PS.: if all you need is a `git clone`, [please go here](https://github.com/erikaheidi/minicli)._

This is **part 1** of the **Building Minicli** series.

### Prerequisites

In order to follow this tutorial, you'll need `php-cli` installed on your local machine or development server, and [Composer](https://getcomposer.org) for generating the autoload files.

## 1. Setting Up Directory Structure & Entry Point

Let's start by creating the main project directory:

```
mkdir minicli
cd minicli
```

Next, we'll create the entry point for our CLI application. This is the equivalent of an `index.php` file on modern PHP web apps, where a single entry point redirect requests to the relevant Controllers. However, since our application is CLI only, we will use a different file name and include some safeguards to not allow execution from a web server.

Open a new file named `minicli` using your favorite text editor:

```
vim minicli
```

You will notice that we didn't include a `.php` extension here. Because we are running this script on the command line, we can include a special descriptor to tell your shell program that we're using PHP to execute this script.


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

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

echo "Hello World\n";
```

The first line is the application *shebang*. It tells the shell that is running this script to use `/usr/bin/php` as interpreter for that code.

Make the script executable with `chmod`:

```
chmod +x minicli
```

Now you can run the application with:

```
./minicli
```

You should see a `Hello World` as output.

## 2. Setting Up Source Dirs and Autoload

To facilitate reusing this framework for several applications, we'll create **two** source directories:

- `app`: this namespace will be reserved for Application-specific models and controllers.
- `lib`: this namespace will be used by the core framework classes, which can be reused throughout various applications.

Create both directories with:

```
mkdir app
mkdir lib
```

Now let's create a `composer.json` file to set up *autoload*. This will help us better organize our application while using classes and other object oriented resources from PHP.

Create a new `composer.json` file in your text editor and include the following content:


```
{
  "autoload": {
    "psr-4": {
      "Minicli\\": "lib/",
      "App\\": "app/"
    }
  }
}
```

After saving and closing the file, run the following command to set up autoload files:

```
composer dump-autoload
```

To test that the autoload is working as expected, we'll create our first class. This class will represent the Application object, responsible for handling command execution. We'll keep it simple and name it `App`.

Create a new `App.php` file inside your `lib` folder, using your text editor of choice:

```
vim lib/App.php
```

The `App` class implements a `runCommand` method replacing the "Hello World" code we had previously set up in our `minicli` executable. 
We will modify this method later so that it can handle several commands. For now, it will output a "Hello $name" text using a parameter passed along when executing the script; if no parameter is passed, it will use `world` as default value for the `$name` variable.

Insert the following content in your `App.php` file, saving and closing the file when you're finished:

```
<?php

namespace Minicli;

class App
{
    public function runCommand(array $argv)
    {
        $name = "World";
        if (isset($argv[1])) {
            $name = $argv[1];
        }
        
        echo "Hello $name!!!\n";
    }
}
```

Now go to your `minicli` script and replace the current content with the following code, which we'll explain in a minute:


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

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

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

use Minicli\App;

$app = new App();
$app->runCommand($argv);

```

Here, we are requiring the auto-generated `autoload.php` file in order to automatically include class files when creating new objects. After creating the `App` object, we call the `runCommand` method, passing along the global `$argv` variable that contains all parameters used when running that script. The `$argv` variable is an array where the first position (0) is the name of the script, and the subsequent positions are occupied by extra parameters passed to the command call. This is a [predefined variable](https://www.php.net/manual/en/reserved.variables.argv.php) available in PHP scripts executed from the command line.

Now, to test that everything works as expected, run:

```
./minicli your-name
```

And you should see the following output:

```
Hello your-name!!!
```

Now, if you don't pass any additional parameters to the script, it should print:

```
Hello World!!!
```

## 3. Creating an Output Helper

Because the command line interface is text-only, sometimes it can be hard to identify errors or alert messages from an application, or to format data in a way that is more human-readable. We'll outsource some of these tasks to a helper class that will handle output to the terminal.

Create a new class inside the `lib` folder using your text editor of choice:

```
vim lib/CliPrinter.php
```

The following class defines three public methods: a basic `out` method to output a message; a `newline` method to print a new line; and a `display` method that combines those two in order to give emphasis to a text, wrapping it with new lines. We'll expand this class later to include more formatting options.


```
<?php

namespace Minicli;

class CliPrinter
{
    public function out($message)
    {
        echo $message;
    }

    public function newline()
    {
        $this->out("\n");
    }

    public function display($message)
    {
        $this->newline();
        $this->out($message);
        $this->newline();
        $this->newline();
    }
}
```

Now let's update the `App` class to use the CliPrinter helper class. We will create a property named `$printer` that will reference a `CliPrinter` object. The object is created in the `App` constructor method. We'll then create a `getPrinter` method and use it in the `runCommand` method to display our message, instead of using `echo` directly:

```
<?php

namespace Minicli;

class App
{
    protected $printer;

    public function __construct()
    {
        $this->printer = new CliPrinter();
    }

    public function getPrinter()
    {
        return $this->printer;
    }

    public function runCommand($argv)
    {
        $name = "World";
        if (isset($argv[1])) {
            $name = $argv[1];
        }

        $this->getPrinter()->display("Hello $name!!!");
    }
}
```

Now run the application again with:

```
./minicli your_name
```

You should get output like this (with newlines surrounding the message):

```

Hello your_name!!!


```

In the next step, we'll move the command logic outside the `App` class, making it easier to include new commands whenever you need.

## 4. Creating a Command Registry

We'll now refactor the `App` class to handle multiple commands through a generic `runCommand` method and a *Command Registry*. New commands will be registered much like *routes* are typically defined in some popular PHP web frameworks. 


The updated `App` class will now include a new property, an array named `command_registry`. The method `registerCommand` will use this variable to store the application commands as anonymous functions identified by a name.

The `runCommand` method now checks if `$argv[1]` is set to a registered command name. If no command is set, it will try to execute a `help` command by default. If no valid command is found, it will print an error message.

This is how the updated `App.php` class looks like after these changes. Replace the current content of your `App.php` file with the following code:

```
<?php

namespace Minicli;

class App
{
    protected $printer;

    protected $registry = [];

    public function __construct()
    {
        $this->printer = new CliPrinter();
    }

    public function getPrinter()
    {
        return $this->printer;
    }

    public function registerCommand($name, $callable)
    {
        $this->registry[$name] = $callable;
    }

    public function getCommand($command)
    {
        return isset($this->registry[$command]) ? $this->registry[$command] : null;
    }

    public function runCommand(array $argv = [])
    {
        $command_name = "help";

        if (isset($argv[1])) {
            $command_name = $argv[1];
        }

        $command = $this->getCommand($command_name);
        if ($command === null) {
            $this->getPrinter()->display("ERROR: Command \"$command_name\" not found.");
            exit;
        }

        call_user_func($command, $argv);
    }
}
```

Next, we'll update our `minicli` script and register two commands: `hello` and `help`. These will be registered as anonymous functions within our `App` object, using the newly created `registerCommand` method.

Copy the updated `minicli` script and update your file:

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

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

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

use Minicli\App;

$app = new App();

$app->registerCommand('hello', function (array $argv) use ($app) {
    $name = isset ($argv[2]) ? $argv[2] : "World";
    $app->getPrinter()->display("Hello $name!!!");
});

$app->registerCommand('help', function (array $argv) use ($app) {
    $app->getPrinter()->display("usage: minicli hello [ your-name ]");
});

$app->runCommand($argv);

```

Now your application has two working commands: `help` and `hello`. To test it out, run:

```
./minicli help
```

This will print:

```

usage: minicli hello [ your-name ]


```

Now test the `hello` command with:

```
./minicli hello your_name
```

```

Hello your_name!!!


```


You have now a working CLI app using a minimalist structure that will serve as base to implement more commands and features.

This is how your directory structure will look like at this point:

```
.
├── app
├── lib
│   ├── App.php
│   └── CliPrinter.php
├── vendor
│   ├── composer
│   └── autoload.php
├── composer.json
└── minicli

```

In the next part of this series, we'll refactor `minicli` to use **Command Controllers**, moving the command logic to dedicated classes inside the application-specific namespace. See you next time!