Building and Distributing a Command Line PHP Application

How to pack a command line php application for distribution

Recently I wanted to create a small php command line application for managing my BitBucket repositories.

Making a cli executable with one file is simple.

But usually you need more than that. You want to use composer packages, to provide a good interface for user, to be able to parse arguments and options and to print a help messages.

That means you will have many files - your source code plus packages or complete framework.

To have the whole project build into a single executable file (phar - PHP Archive) you need to pack it somehow and the solution is phar-composer.

Command line application

Instructions

Phar-composer (which itself is a phar) can pack any composer managed project. So you need to have a composer.json file in your root directory.

Make a new directory cli-app, initialize composer project with composer init and fill in the questions to generate new composer.json.

mkdir cli-app
cd cli-app
composer init

After completing the process you should have a composer.json with content like:

{
    "name": "dam1r89/cli-app",
    "authors": [
        {
            "name": "Damir Miladinov",
            "email": "damir89@gmail.com"
        }
    ],
    "require": {}
}

Run a composer install

Even if you are not using additional packages you need to run composer install or phar-composer build will complain

Before packing a project we need to specify what will be our main php file. What Phar-composer is doing is reading composer's binary property bin defined in the project's composer.json file. In simple words the bin key is used as a pointer to the main file for starting whole application. So we need to add something like this:

{
    "name": "dam1r89/cli-app",
    "authors": [
        {
            "name": "Damir Miladinov",
            "email": "damir89@gmail.com"
        }
    ],
    "require": {},
+   "bin": ["bin/main"]
}

For this example main file will be (even it doesn't have extension) a php file.

Make the bin directory

mkdir bin

And create bin/main file.

<?php

// To use composer's autoload
require_once '../vendor/autoload.php';

// $argv contains command line arguments
print "From packed application: " . $argv[1] . "\n";

Phar-composer will set file permission based on the permissions of this file. So we want to make it executable:

chmod +x bin/main

Download phar-composer in to the project directory. Make a new directory dist (distributable) which will hold the project compressed into a phar executable.

mkdir dist

Finally run the build with

php phar-composer.phar build . dist

The dot means build the project from current directory (needs to have composer.json), and dist is where file will be saved. In dist folder you will have a phar file with name of your project.

Tree

To test it run php dist/cli-app.phar "Woohoo". You should see From packed application: Woohoo.

Now you can distribute cli-app.phar as a single file executable application.

System wide access

If you want to make application globally available you can do something like

cp dist/cli-app.phar /usr/local/bin/cli-app

And then use application like cli-app "Hello".

This previous step can be done automatically with phar-composer install command.

php phar-composer.phar install . cli-app

Last parameter can be used to specify the command name.

More complex application

When building a real world application you will want to have a separate directory for your source code and only initialize commands through the main file. For example you would have app directory and autoload it with composer:

Example

{
    "authors": [
        {
            "email": "damir89@gmail.com",
            "name": "Damir Miladinov"
        }
    ],
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    },
    "bin": [
        "bin/main"
    ],
    "name": "dam1r89/cli-app",
    "require": {}
}

app/Application.php

  <?php

  namespace App;

  class Application{

    public function say($something){
        print "From packed application: $something\n";
    }
  }

bin/main

<?php

    require_once '../vendor/autoload.php';

    $app = new App\Application();
    $app->say($argv[1]);

Libraries for interacting with user

Previous part explained how to pack the application. What is next is to parse arguments and options and print output. All that can be heavy task unless using a library.

My suggestion is

  • Symfony Console or
  • Laravel Console which is basically a Symfony Console wrapper that provides fluent command definitions like this: command {arg1} {arg2} {--option=}.

Author

I plan to write more articles about common laravel components. If you are interested let’s stay in touch.
comments powered by Disqus