How to Use ZFTool Diagnostics To Ensure Your Modules Work

ZFTool Diagnostics Ran Successfully

Do you want to be sure that when you create Zend Framework 2 modules, that they’ll work in whatever environment they’re used in? At the very least, do you want a simple way for users to check, as well as something that’s self-documenting?

If so, you’re in the right place. Last year, I gave a basic introduction to ZFTool, which is a command line tool to manage applications written in Zend Framework 2.

In that post, I briefly touched on the diagnostics component, which as the name implies, allows for diagnostic checks to be run on a module, much like we’d do so on a computer, car or aircraft.

NB: If you’ve not read the post, do so now as it lays the foundation for this post.

Diagnostics, as the name implies, helps diagnose any issues that may impede the running of your application, helping ensure it always works as expected. The dependencies can be any number of things, including:

  • Are one or more processes running
  • Are one or more extensions loaded in PHP
  • Filesystem Information: disk free space and directory permissions
  • Do classes exist
  • Running services
  • PHP Version
  • File validation checks: can perform checks on INI, JSON, XML and YAML files
  • Attempt to fetch a service from the ServiceManager
  • And loads more

In addition to this we can write our own diagnostic checks, using the Success, Failure and Warning classes. So in today’s tutorial, I’m going to show how to add diagnostics support to a module, checking the following areas:

  • The Apache and PostgreSQL processes are available and running
  • The pdo and pdo_pgsql extensions are installed
  • The cache and log directories are available and able to be written to
  • The Zend OPcache extension is installed and enabled
  • PHP has a minimum version of 5.3.0

 1. Add Diagnostics Support

Despite what’s included in the online documentation, some classes appear to be missing, if you only have "zendframework/zftool": "dev-master", in composer.json.

So we need to add in support for the extra diagnostic classes, which aren’t available in the standard install. To do so, add the following to composer.json and run composer update:

"zendframework/zenddiagnostics": "1.0.*@dev"

With that done in your module’s Module.php, add in the following use statements:

use ZendDiagnostics\Check\DirWritable;
use ZendDiagnostics\Check\ExtensionLoaded;
use ZendDiagnostics\Check\ProcessRunning;
use ZendDiagnostics\Check\PhpVersion;

2. The Diagnostics Function

After this, add a new function, getDiagnostics() at the end of Module.php, which you can find in this Gist. Let’s step through it now.

public function getDiagnostics()
{
return array(
  'Cache & Log Directories Available' => function() {
    $diagnostic = new DirWritable(array(
      __DIR__ . '/../../data/cache',
      __DIR__ . '/../../data/log'
  ));
  return $diagnostic->check();
},

Here, we add a check to verify that two directories, data/cache and data/log are available in the project’s root. This check ensures that the directory both exists and is writable. As we’re checking multiple directories, we passed in an array, listing each one. This makes it nice and concise.

'Check PHP extensions' => function(){
  $diagnostic = new ExtensionLoaded(array(
    'json',
    'pdo',
    'pdo_pgsql',
    'intl',
    'session',
    'pcre',
    'zlib',
    'apc',
    'apcu',
    'Zend OPcache'
));
return $diagnostic->check();
},

This check, verifies that the json, pdo, pdo_pgsql, intl, session, pcre, zlib, and Zend OPcache extensions are loaded in PHP. As with the DirWritable, we can specify multiple extensions at once, by passing in an array of them, keeping the diagnostic code simple.

NB: These extensions are rather arbitrary and were relevant to a specific project I was working on recently. So feel free to either keep or change them as suits your needs.

'Check Apache is running' => function(){
    $diagnostic = new ProcessRunning('apache2');
    return $diagnostic->check();
},
'Check PostgreSQL is running' => function(){
    $diagnostic = new ProcessRunning('postgresql');
    return $diagnostic->check();
},
'Check Memcached is running' => function(){
    $diagnostic = new ProcessRunning('beanstalkd');
    return $diagnostic->check();
},

In these three checks, we’re verifying that the apache2, postgresql and beanstalkd processes are running. This check accepts both a process name and process id (PID).

I’m running mine on Debian Wheezy. So if you’re using another Linux distro, Windows, Solaris or BSD, then remember to change the names as required.

'Check PHP Version' => function(){
    $diagnostic = new PhpVersion('5.3.0', '>=');
    return $diagnostic->check();
}
);
}

Finally, I’ve added in one last check to verify that the installed version of PHP is at or greater than 5.3.0. Now we can specify this in composer.json, but it’s handy to know how to do it here as well.

3. Running Module Diagnostics

Ok, with the diagnostics support added and the diagnostics function written, let’s run it so you see what it looks like. From the project’s root directory, run the following command, which will run the diagnostics function we’ve just created:

vendor/bin/zf.php diag --debug <your module name>

Assuming that all the services were running, extensions were loaded and PHP had a minimum version of 5.3.0, you’ll see output similar to the screenshot below:

ZFTool Diagnostics Ran Successfully

ZFTool Diagnostics Ran Successfully

NB: To be fair, the extensions and PHP version check aren’t strictly necessary, as we can, to a degree, enforce these in composer.json, as we have. But for the purposes of an example, and making it really obvious what’s required for the module to run, it’s worth adding here.

You can see in the output above, the tests which were run and what they were checking for, along with the check status; which was an OK in each case. What’s great, IMHO, is that the diagnostics are so verbose. So it’s extremely clear as to what’s being checked and how it went.

4. When Checks Fail

But what if one of the services wasn’t running or some of the extensions weren’t loaded?

Now I’m going to update the getDiagnostics function, adding in some extensions which aren’t available as well as some services which aren’t running in my test environment. Specifically, I’ll add in checks for APC and APCu, as well as MongoDB and Postfix. Running the diagnostics again, here’s what happened.

ZFTool Diagnostics Fail To Run

ZFTool Diagnostics Fail To Run

In the screenshot above, you can see, similar to output in PHPUnit, that there were 3 failed checks, and which ones they were, which we fully expected to see. From here, you can quickly find what to change in your environment.

Wrapping Up

So there you have it. With a little bit of effort, we can add in diagnostics for the modules we write, which can be helpful to both us and other developers who use our modules.

These diagnostics can help all of us know both what the dependencies are for our modules, as well as if our environment satisfies them. No longer do we need to hunt through source code to know whether everything required is available. In one file, we can store it all and provide a scriptable interface to verify it.

In next week’s tutorial, we’ll cover writing your own diagnostic classes, with an example of running php lint on a module’s config file. So stay tuned for that.

Are you using diagnostics in your modules? If not, why not? Either way, share your thoughts in the comments.

Further Information

Do check out the online documentation so that you know about the full range of options available.

ZFTool