Posts Tagged programming

Revisiting PHP Command-Line Scripting

A few years ago I blogged about “Using Silex for Console Tasks”, but times have changed and so have I. I still use Silex for projects, but when it comes to console commands, I’ve found a new toy. You see, the Symfony Console Component can be a bit cumbersome – it groups your console commands as though they were all a single application; but, when I’m writing a script or a system utility, I don’t need an entire application – I just need one command, with one set of arguments, and one help screen.

php-GetOptionKit, from github user c9s, is a very simple library that has many of the features I’ve wished for in a PHP CLI toolkit – and, as a bonus, it’s very fast to get wired up using Composer.

The Bootstrap

OK, so, let’s do a quick walk-through for bootstrapping a project using Composer and GetOptionKit.

First, create a folder for your project. Let’s call this one “getoptionkit-skeleton”.

$> mkdir getoptionkit-skeleton
$> cd getoptionkit-skeleton

Inside that folder, get composer from http://www.getcomposer.org (or you can follow the “global installation” instructions to store it outside the folder).

curl -s https://getcomposer.org/installer | php

As a security-conscious software developer, you should immediately recognize that the above command is dangerous; it’s quite literally “Remote/Arbitrary Code Execution”.

Nevertheless, folks seem to be confident in the security of the composer system, so for the sake of the tutorial we will forge ahead.

Once composer is in place, we must define the composer.json for our project. Begin by executing this command:

$> composer init

This will begin the interactive composer configuration process:

Welcome to the Composer config generator

This command will guide you through creating your composer.json config.

Package name (/) [root/getoptionkit-skeleton]: beryllium/getoptionkit-skeleton
Description []: A quick start package for creating CLI scripts using GetOptionKit
Author [Kevin Boyd ]: (I pressed enter to accept the default)
Minimum Stability []: dev

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? (I pressed enter to say yes)

Composer then asks you to search for a package. I typed “c9s/getoptionkit” and waited a bit for the result. I chose the option for “dev-master”, because it’ll be the latest and greatest (or brokenest. We shall see.)

I said “no” for defining development dependencies, and then said “yes” to generate the composer.json file:

{
  "name": "beryllium/getoptionkit-skeleton",
  "description": "A quick start package for creating CLI scripts using GetOptionKit",
  "require": {
    "c9s/getoptionkit": "dev-master"
  },
  "authors": [
    {
      "name": "Kevin Boyd",
      "email": "my.example.email@example.com"
    }
  ],
  "minimum-stability": "dev"
}

At this point, I typed “composer install” to install my vendor dependencies.

Now it’s time to create the bootstrap.php file:

<?php

if (php_sapi_name() != 'cli') {
die('Must run from command line');
}

error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
ini_set('html_errors', 0);

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

function writeln( $msg = '' )
{
echo $msg . "\n";
}

I like to define the writeln() function as a quick shortcut, you could just as easily leave it out or call it something else.

The Fun Part

Now we can begin creating our command line program. I’m going to build a simple script called “bin/hello” with a few features to give GetOptionKit a workout.

Naturally, at this point you should make a “bin” directory and begin editing a file inside it called “hello”. Remember to chmod a+x bin/hello, to make it easy to execute.

#!/usr/bin/env php
<?php
require_once __DIR__ . '/../bootstrap.php';
$app = new GetOptionKit\GetOptionKit();
$app->add( 'h|help', 'Display usage instructions' ); // The '|' allows you to define a short and a long form: -h and --help, in this case
$app->add( 'g|greeting:', 'Greeting (default: "Hello")' );

$options = $app->parse( $argv );
$arguments = $options->getArguments();
array_shift($arguments); //Strip off the command name

if ( $options->has( 'help' ) || count( $arguments ) != 1 )
{
  writeln( 'Usage: hello [-g "Greetings"] yourname' );
  writeln();
  $app->printOptions();
  writeln();
  exit(0);
}

//Set the default greeting
$greeting = 'Hello';

//Check for the optional override
if ( $options->has('greeting') )
{
  $greeting = $options['greeting']->value;
}

writeln( $greeting . ', ' . $arguments[0] . '!' );

Now at this point, the script can be flagged as executable (chmod a+x bin/hello) and launched. You should see the following:

$> bin/hello
Usage: hello [-g "Greetings"] yourname

    -h, --help Display usage instructions
    -g, --greeting  Greeting (default: "Hello")

And now you can give it a spin:

$> bin/hello world
Hello, world!
$> bin/hello -g "G'day" world
G'day, world!
$> bin/hello -g world
Usage: hello [-g "Greetings"] yourname

-h, --help Display usage instructions
-g, --greeting  Greeting (default: "Hello")

You might wonder why the last one failed – that’s because the conditions checked in bin/hello didn’t evaluate to true. After parsing for the value of the “greeting” option, there weren’t enough arguments left to define “yourname”, so the script aborted and showed the help instead. You could set up the evaluation logic in whichever way suits your workflow, of course.

I like GetOptionKit because it lets me quickly spin up a command-line script that behaves similarly to the Unix/Linux/Gnu/BSD utilities I’m accustomed to.

If you’re wanting a shortcut to get this up and running even faster, now you can use my skeleton:

$> composer create-project beryllium/getoptionkit-skeleton myproject

Notes:

  • In order to get the getoptionkit-skeleton working as an installable composer package, I had to modify my composer.json and change the dev-master reference to “1.*” and remove the “minimum-stability” line and preceding comma. I also had to push a tag called 1.0.0 to my github repository.
  • If you are interested in a recent article about using Symfony Console Component, I recommend this post from dcousineau.

, , , , , ,

No Comments

Mailstrom email tool

There comes a time in every developer’s life when he/she is simultaneously bored and annoyed at something.

Usually it’s on a day ending in “y”.

Anyway, the thing that piqued my annoyance this afternoon was Amazon SES and all the hacks I’ve had to do in the past to get sendmail/postfix to relay via it. They are both very much the wrong tool to use for that, but due to various business requirements, I did what I had to do to get them to work.

But, I always felt uneasy about it, especially since it seemed to create more of a problem than it solved. But now, I have created the first part of a solution to that problem: Mailstrom.

Mailstrom is a quick-and-simple command-line PHP script that allows users to send email via Amazon SES. It gives two ways of doing this: As an option on the command line, or by piping the message body to the command. The Readme has installation, configuration, and usage instructions.

Mailstrom was built using Composer, Amazon AWS SDK for PHP, and php-cli-tools from James Logsdon.

My future plans for it include raw message support, as well as Amazon SNS support. I might also consider a queuing/spooling system using SQS, but that would be a very long-term idea since I don’t have a viable use-case for that complexity at the moment.

In the meantime, it works as a simple tool that gets the job done. And that’s the best way to eliminate annoyance.

, , , , ,

No Comments

Phun with Phing, Part One

This week at work, I got to do some fun things with Phing, the build system/tool for PHP. Using the powerful AdHocTasks feature, I was able to implement two quick solutions that were not provided by the stock installation.

The first (covered here in part one) was script mutexing, which is important when scheduling tasks to repeat often. It helps prevent overlapping executions. In part two, I’ll show you how to make an Amazon SNS Notification task that you can use both from your build targets and from the command line to send alerts via Amazon Simple Notification Service.

These two posts presume that you are somewhat familiar with Phing or Ant.

Mutexing

As part of the environment I’m trying to set up at work, I needed to ensure that multiple executions of the same target would not be harmful to the system. The general solution to that is Mutexing (creating a mutual-exclusion “lock”), but there wasn’t any sort of native support for that in Phing.

Using AdHoc, I created a class that could handle a simple Process-ID-based process lock concept. This means that it records the current PID to a temporary file, and as long as that file exists, subsequent executions of the same target will fail. If the build process happens to die in the middle of execution, future executions will examine the PID contained in the lock file and discover that the process no longer exists – then they will overwrite the PID file with the current PID.

It’s not the perfect solution, and I’m sure it could have a bug or two, but if you’re in the same boat as me – maybe it will be helpful for you, too :)

Example AdHocTask/PHP Source

<!-- Script mutex, to prevent multiple simultaneous executions -->
<adhoc-task name="mutex"><![CDATA[
class BerylliumMutex extends Task {
  private $pidfile;
  private $clear = false;

  function setPidfile( $pidfile )
  {
    $this->pidfile = $pidfile;
  }

  function setClear( $clear )
  {
    $clear = strtolower( $clear );
    if ( true == $clear || "true" == $clear || "yes" == $clear || "clear" == $clear )
    {
      $this->clear = true;
    }
    else
    {
      $this->clear = false;
    }
  }

  function getPidfile()
  {
    if ( !empty( $this->pidfile ) )
    {
      return $this->pidfile;
    }
    else
    {
      $pidfile = $this->getProject()->getProperty( 'config.mutex' );
      if ( !empty( $pidfile ) )
      {
        $this->pidfile = $pidfile;
        return $pidfile;
      }
      else
      {
        throw new BuildException( "Unable to get Pidfile; please set config.mutex in your build properties" );
      }
    }
  }

  function main()
  {
    $pid = getmypid();
    $pidfile = $this->getPidfile();

    if ( $this->clear )
    {
      if ( file_exists( $pidfile ) )
      {
        unlink( $pidfile );
        $this->log( 'Deleted mutex/pid: ' . $pidfile );
      }
      else
      {
        $this->log( 'Mutex/pid not found; nothing to do' );
      }

      return; //We're done here.
    }

    if ( file_exists( $pidfile ) )
    {
      $cur_pid = file_get_contents( $pidfile );

      if (
        is_numeric($cur_pid)  &&
        false != posix_getsid( $cur_pid )
      )
      {
        throw new \BuildException( 'Build is already in progress.' );
      }
    }

    $this->createPid();
  }

  function createPid()
  {
    $pidfile = $this->getPidfile();
    file_put_contents( $pidfile, getmypid() );
    $this->log( "Created mutex/pid: " . $pidfile );
  }
}
]]></adhoc-task>

How to use it

The key to using this is to identify the critical path that needs to be mutexed; in my case, it was an “update” cronjob that had to do several filesystem-related tasks. You might be interested in making a “meta-target” that uses <phingcall> to collect related tasks into a safely-mutexed process.

<property name="config.mutex" value="/tmp/tmp-phing-mutex.pid"/>
<target name="update">
  <mutex/>
    <echo msg="Doin' stuff"/>
    <phingcall target="dist"/>
    <echo msg="Doin' more stuff"/>
    <phingcall target="deploy-remote-production"/>
    <echo msg="Done doin' stuff"/>
    <phingcall target="order-ice-cream"/>
  <mutex clear="true"/>
</target>

Well, there you have it. Mutexing. I hope you find it useful, and stay tuned for Part Two – Amazon SNS Notifications.

Edited: Fixed “/adhoc” to “/adhoc-task”, and added a new clause to the setClear() logic to make the code compatible with the newest Phing release.

, , ,

1 Comment

PHP Annotations

Annotations are all the rage in some PHP circles these days, but I don’t like them. I try to avoid them as much as possible, and I hope that my reasoning for doing so is justified.

What are annotations?

Annotations are a form of meta-code; in practice, they allow you to modify attributes and behaviors of your methods and classes in fast and convenient ways. Three common uses for annotations in symfony2 are routing, validation, and ORM binding – although regular users of PHPUnit will also be familiar with annotations for data providers and test grouping.

Why are they awesome?

Annotations do have some good qualities. They allow you to reduce the clutter of your projects by folding various configuration and behavioural information into the class itself. For example, the average symfony2 bundle has a validation.yml and routing.yml file – both of these files can be eliminated or reduced in complexity by using annotations.

Additionally, annotations allow you to design and maintain your projects in a more straightforward manner. Instead of having to open one class file and a handful of configuration files (or a handful of supporting classes), all the information is right there within the primary class file.

You might be interested in this article on how to build custom PHP annotations, to see some of the power and flexibility they can bring to your projects.

Why am I avoiding them?

I must confess that I have a semi-irrational paranoia about using annotations. However, I feel that there are also some rational reasons to consider avoiding them.

One such reason is that the decentralization of certain types of configuration can lead to oversights and possible repetition of unanticipated mistakes, which can cause behavior that is unpredictable and hard to catch and debug. While the term “decentralization” might seem out of place when talking about merging code and configuration together, it helps to remember that a symfony2 bundle (or any PHP project) can have many controllers and classes and entities. You might end up with two controllers fighting over routing (an easy-to-solve issue, I’m sure), or you might find yourself struggling to remember a route name and which controller handles it (a situation where a single routing definition can help your code be more self-documenting).

There are also some speed concerns with annotations, due to the heavy use of Reflection, but they are mostly alleviated through the proper use of caching.

Most importantly, I feel that it is wrong to have what amounts to program logic tucked away in what otherwise looks like simple comments. If PHP had an official non-comment-like notation for annotations, I might be more inclined to use them. As it stands, I only use annotations in unit tests at the moment – not in the main/production code.

That’s pretty much the crux of what I don’t like about them: I do not like using comments as code.

How about you?

, , , ,

2 Comments