PHP Readline Auto Completion

I am currently working on a set of server utilities that are meant to help me and a few of my friends manage their hosting environments. In the process of developing this, I decided to start using the php readline functions. The biggest benefit of readline over STDIN is the ability to do tab completion. This will be very helpful when trying to configure different services, assign users to configuration, and a host of other tedious work.

By default, auto completion in readline acts just like it does on a shell. Readline will try to use the local filesystem and the current directory the script is using to try and load directories and files. Using the readline completion function you can assign custom functionality to allow readline to return an array of matches. It is also important to note that you do not have to actually filter out the array of options that is returned, as readline will already do this for you. This makes it very easy to use, as well as very powerful when trying to help users get around your php shell script.

As your application gets more complex, you may find a need to work with the auto completion register in different ways.There are 3 ways that you might want to create your own auto complete functions, and Iâll show you a quick example of how each of the them would work.
Function

The first, and easiest way is to use a normal function. Take the following example:

      /**
      * Simple readline callback function
      */
      $fruits = array(
         'apple',
         'banana',
         'orange',
         'peach'
      );

      function callback_fruit($input, $index) {
         global $fruits;
         return $fruits
      }

      readline_completion_function('callback_fruit');
      $FNinput = readline("(fn) Fruit: ");
      print "Function picked: {$FNinput}\n";

The function is very simple, however you can add any type of logic you need to get your list. There are also helper functions that can quickly help you build more complex logic based on the type of input that is given. If you run this code from the command line, you will be greeted with a prompt. At the prompt if you hit tab twice, you will see a list of the four fruits that are available. If you type the letter âaâ and hit tab, apple will appear and you can hit enter. This will let you know that your function picked apple.
Object

If you are working inside of a framework, you may need to have an object. There are two ways that you can do this. One would be to use the readline_completion_function externally and pass in the instance of the object, and the method name as an array. But I prefer to keep the logic tightly coupled, so I create a method inside of the object instance to handle the registration. Either way will work, and you can change it up if needed.

      /**
      * Simple readline callback object
      */
      class Fruit {
              public $fruit = array(
                      'apple',
                      'banana',
                      'orange',
                      'peach'
              );

              public function register_callback() {
                      readline_completion_function(array($this, 'callback'));
              }

              public function callback($input, $index) {
                      return $this->fruit;
              }
      }

      $fr = new Fruit;
      // You could replace this callback with:
      // readline_completion_function(array($fr, 'callback'));
      $fr->register_callback();
      $OBJinput = readline("(obj) Fruit: ");
      print "Object picked: {$OBJinput}\n";

Static Object

Some people consider static objects the worst possible thing you could do short of setting smokey the bear on fire. However, using a singleton for completion is just as simple as using a standard object.

      /**
      * Simple readline callback static object
      */
      class staticFruit {
              public static $fruit = array(
                      'apple',
                      'banana',
                      'orange',
                      'peach'
              );

              public static function register_callback() {
                      readline_completion_function(array('staticFruit', 'callback'));
              }

              public static function callback($input, $index) {
                      return self::$fruit;
              }
      }

      // Static
      staticFruit::register_callback();
      $STAinput = readline("(static) Fruit: ");
      print "Static picked: {$STAinput}\n";

Conclusion

Auto completion with readline is really that simple. This really opens up a lot of possibilities for building better interactive shell scripts with PHP.