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.

PHP Tip: Quick and Easy Variable Debug Display

When developing or prototyping code, I find that I need a quick dump of information about the code I’m working on. This is not limited to just variables, but also function returns and other resources. I created this tiny function that can handle multiple variables and if the first parameter is the name of an export function it will use that for display.

 

function DumpVariables() {
   // Get all of our variables
   $Vars = func_get_args();
   $VarCount = count($Vars);

   // Determine dump function
   switch ($Vars[0]) {
      case 'print_r':
      case 'var_export':
         $DumpFunction = array_shift($Vars);
         $VarCount--;
         break;

      default:
         $DumpFunction = 'var_dump';
   }

   // Make our divider
   $Line = str_repeat('-', 30);

   foreach ($Vars AS $Sequence=>$Var) {
      // Handle the first pass of the data
      if (!isset($Check)) {
         // Test for a shell to determine a web or shell environment
         if (!isset($_SERVER['SHELL'])) {
            print "<pre>\n";
         }
         // Header
         print "Dumping {$VarCount} variables ".date('Y-m-d H:i:s')."\n";
         $Check = true;
      }
      // Variable Header
      print "\n\n {$Line} Variable {$Sequence} {$Line}\n\n";
      $DumpFunction($Var); // Dump the variable
    }
    print "\n\n {$Line} End of Dump {$Line}\n"; // Footer
    exit(0); // Kill the process because we are debugging
 }

// Examples
DumpVariables('print_r', $myVar, get_class_methods($myClass), New MyClass);
DumpVariables($Session, $PostData, $DatabaseRecord->toArray());

Notifying your development team when a git commit is made

My last writeup, looked at how to write a pre-commit hook for your git repository that would not allow you to submit broken PHP code. This time, I’ll show you how to use the .git/hooks/post-commit hook in order to notify all of your development members when a commit has been made successfully. It is very important to understand the environment that git runs in. In my current development setup, only trusted developers get access to git and the helper scripts. By the time I have a hijacked rogue developer account messing with our repositories, I have much bigger problems on my hand. This is why I have setup a common place on my server to store helper scripts and other utilities that any of our developers can use. This is by no means the NSA brand secure way of doing things, but that discussion is a whole other topic.
Read more »

PHP Syntax Checker For Git Update. Test Only Files In The Commit

After my first article on working with git pre-commit hooks, I found a small issue that could cause a developer not to commit, even if all of the code they want to commit is ok. The problem comes with files that are recognized as change, but will not be part of the commit. Luckily sed has an easy solution to this problem. My new git hook now looks like this:

git status |  sed '/# Changed but not updated:/,$d' | sed '/# Untracked files:/,$d' |cut -c 3- | egrep  '^(modified|new file)' | egrep '\.(php|html)$' | awk -F: '{ gsub(/[[:space:]]*/,"", $2); print $2}' | xargs -n1 -r php -l

By using sed, we knock out every line below the “Changed but not updated”, and “Untracked files” sections in the commit message. This way, if you have other sections of code that you are working on, but not pushing into this commit.

PHP syntax checker for Git repository changes

So I was reading this article about writing a php lint checker, that would check your php files for syntax errors before commit. Since I use git, I decided to write something similar. I put this in .git/hooks/pre-commit and made that hook executable. So far it’s working very well.

git status | cut -c 3- | egrep  '^(modified|new file)' | egrep '\.(php|html)$' |awk -F: '{ gsub(/[[:space:]]*/,"", $2); print $2}' |xargs -n1 php -l

In Linux, you can take the output of one command, and make it the input of the next command using the pipe (shift \) character. So let’s break down this command and see how this helps us keep developers from committing php code with errors.
Read more »

PHP 5.3 Deprecation Detector

With the release of PHP 5.2.14, the PHP 5.2.x branch has reached end of support. Also many people in the community pushing very hard for php 5.3 adoption. In a perfect world, this would be a very easy process and all of your code would just roll over and work perfectly. This however is nothing like a perfect world. If you use a managed hosting solution, many are still requiring you use php 5.1 since it is the latest stable release in the RHEL repositories. And if that wasn’t enough, php 5.3 deprecates a decent bit of functionality that many older scripts rely on. You can find a list of deprecated functionality in the php manual.

The functionality still works in PHP 5.3, however it now throws a E_DEPRECATED error. I’ve read many blogs who say the best fix for this is to use the following:

// WRONG !!!!!
error_reporting(E_ALL &~ E_DEPRECATED);

Sadly, this code only hides the messages from you and does nothing to actually help solve the problem. This would be like telling someone who is being chased by your favorite movie monster to just close their eyes and they won’t get hacked to bits! That doesn’t work in the movies, and I assure you it doesn’t work in your production environment either. Luckily while the monster is hacking your friend to bits you can quickly put in place a solution that will help you fix these problems using only MySQL and standard PHP functionality.
Read more »

New Blog

Things at work are finally calming down a little bit! Now I can finally update my blog, and move it to the new cluster at work. I should have everything ported over to the new setup shortly, so stay tuned. I will also have a new blog post coming up shortly, so stay tuned!