The best part about working with talented PHP Developers in the workplace is learning techniques to make us better developers.

Last week I was required to create an automated PHP script to convert a data file of a legacy application into a newly created internal web app. After completing it, a workmate of mine; Tom McDonnell informed me of a script he had developed which accepted command line arguments and provided great feedback of the status of the running script.

With Tom’s permission to publish components of his source code, I would like to share these tips in this two part article.

The first step is creating a common cron class called class.my_cron.php
We do not want the class to be cloned or instantiated, we merely wish to use it’s methods freely.

In this example we will offer the user to add three possible parameters to the script via the command line. These are -actual, -hypothetical, -verbose.

Actual = Commit the changes
Hypothetical = Do not commit the changes, but still perform all the correct sql queries prior
Verbose = Display detailed descriptions of anything you believe is relevant for debugging purposes.

<?php
/**
 * This class is to define a common library file to include to help with common cron scripting tasks.
 */

class my_cron
{

  // No cloning or instantiating allowed.
  final private function __construct(){}
  final private function __clone(){}
}
?>

We require a function that will validate the parameters added to class.my_cron.php via the command line and then return an array of two values:

  1. Is the query going to commit or rollback at the end of the process?
  2.  What kind level of debugging do we wish to output in the command line?
We rely on the built-in global variable titled $argc which provides a count of arguments supplied. Note that there is always one argument provided by default, so the count should be what you expect +1.
If one argument is passed (really none), then we display an ASCII  header for fun and the usage message from our original PHP script which calls this method.
If two or three arguments are passed  (really one/two), then we continue through.
If anything else, we print an incorrect number of arguments error. Combined with  an ASCII  header for fun and the usage message from our original PHP script which calls this method.
Depending on the parameter results, we update the two boolean values to be returned by an array.
  public static function validateArgvArgumentsAndDieWithErrorMessageIfInvalid($usageMessage)
  {
    // Declare PHP Super globals
    // http://www.php.net/manual/en/reserved.variables.php
    global $argv; //Array of CLI arguments
    global $argc; //Count of CLI arguments

    $header = self::cronMsgHeader();

    switch ($argc) {
      case 1:
        echo "$header\n$usageMessage";
        exit(0);
      case 2: // Fall through.
      case 3:
        // Expected number of arguments.  Do nothing here, but proceed to next step.
        break;
      default:
        echo "Incorrect number of arguments.\n$header\n$usageMessage";
        exit(0);
    }

    $boolActuallyUpdateDatabase = null;
    $boolVerboseOutput = false;
    $errorString = 'Only one option of -hypothetical or -actual may be used.';
    $header = self::cronMsgHeader();
    for ($i = 1; $i < $argc; ++$i) {
      switch ($argv[$i]) {
        case '-hypothetical':
          if ($boolActuallyUpdateDatabase !== null) {
            echo "$errorString\n\n$header\n$usageMessage";
            exit(0);
          }
          echo "\n$header\n";
          $boolActuallyUpdateDatabase = false;
          break;

        case '-actual':
          if ($boolActuallyUpdateDatabase !== null) {
            echo "$errorString\n\n$header\n$usageMessage";
            exit(0);
          }
          echo "\n$header\n";
          $boolActuallyUpdateDatabase = true;
          break;

        case '-verbose':
          if ($argc < 3) {
            echo "$errorString\n\n$header\n$usageMessage";
            exit(0);
          }
          echo "\n$header\n";
          $boolVerboseOutput = true;
          break;

        default:
          echo "Incorrect argument '{$argv[1]}'.\n$usageMessage";
          exit(0);
      }
    }

    return array($boolActuallyUpdateDatabase, $boolVerboseOutput);
  }
Our message header that we created for fun to display at the top of the script
/**
 * Create a header usageMessage
 * @return string
 */
public static function cronMsgHeader()
{
  $usageMessage = <<<STRHEADR
******************************************************************************
*                                                                            *
*    MM     MM    YY     YY          CCCCCCC   RRRRR     OOOOOOO   NN    N   *
*    M M   M M     YY   YY           C         R    R    O     O   N N   N   *
*    M  M M  M       Y Y             C         R    R    O     O   N  N  N   *
*    M   M   M        Y              C         RRRRR     O     O   N  N  N   *
*    M       M        Y              C         R   R     O     O   N   N N   *
*    M       M        Y              C         R    R    O     O   N   N N   *
*    M       M        Y              CCCCCCC   R     R   OOOOOOO   N    NN   *
*    M       M        Y                                                      *
******************************************************************************
STRHEADR;

  return $usageMessage;
}
We’ll also add another disclaimer method that to display if the user has chosen to add the hypothetical parameter.
  /**
   * Echo's a disclaimer to the end user if running in hypothetical mode
   * @param $boolActuallyUpdateDatabase
   * @return void
   */
  public static function runInHypotheticalMode($boolActuallyUpdateDatabase) {
    if (!$boolActuallyUpdateDatabase) {
      echo "\nNote Regarding Hypothetical Mode\n";
      echo "--------------------------------\n";
      echo 'The script is running in hypothetical mode.  All database changes referred to';
      echo ' in the output below will be rolled back before the script completes.  The changes';
      echo " will also be rolled back should the script be interrupted part way through.\n";
    }
  }

So that’s all for now, in the next instalment I’ll show you how to implement these features into your code that will run the cron file via command line.

The entire cron class should now look like this.

<?php
/**
 * This class is to define a common library file to include to help with common cron scripting tasks.
 */

class my_cron
{

	// No cloning or instantiating allowed.
	final private function __construct()
	{
		//
	}

	final private function __clone()
	{
		//
	}

	/**
	 * Handles the command line arguments and makes sure they contain valid parameters
	 *
	 * @param $usageMessage
	 *
	 * @return array
	 */
	public static function validateArgvArgumentsAndDieWithErrorMessageIfInvalid($usageMessage)
	{
		// Declare PHP Super globals
		// http://www.php.net/manual/en/reserved.variables.php
		global $argv; //Array of CLI arguments
		global $argc; //Count of CLI arguments

		$header = self::cronMsgHeader();
		switch ($argc) {
			case 1:
				echo "$header\n$usageMessage";
				exit(0);
			case 2: // Fall through.
			case 3:
				// Expected number of arguments.  Do nothing here, but proceed to next step.
				break;
				    default:
				echo "Incorrect number of arguments.\n$header\n$usageMessage";
				exit(0);
		}

		$boolActuallyUpdateDatabase = null;
		$boolVerboseOutput = FALSE;
		$errorString = 'Only one option of -hypothetical or -actual may be used.';
		$header = self::cronMsgHeader();
		for ($i = 1; $i < $argc; ++$i) {
			switch ($argv[$i]) {
				case '-hypothetical':
					if ($boolActuallyUpdateDatabase !== null) {
						echo "$errorString\n\n$header\n$usageMessage";
						exit(0);
					}
					echo "\n$header\n";
					$boolActuallyUpdateDatabase = FALSE;
					break;
				case '-actual':
					if ($boolActuallyUpdateDatabase !== null) {
						echo "$errorString\n\n$header\n$usageMessage";
						exit(0);
					}
					echo "\n$header\n";
					$boolActuallyUpdateDatabase = TRUE;
					break;
				case '-verbose':
					if ($argc < 3) {
						echo "$errorString\n\n$header\n$usageMessage";
						exit(0);
					}
					echo "\n$header\n";
					$boolVerboseOutput = TRUE;
					break;
				default:
					echo "Incorrect argument '{$argv[1]}'.\n$usageMessage";
					exit(0);
			}
		}
		return array($boolActuallyUpdateDatabase, $boolVerboseOutput);
	}

	/**
	 * Create a header usageMessage
	 *
	 * @return string
	 */
	public static function cronMsgHeader()
	{
		$usageMessage = <<<STRHEADR
******************************************************************************
*                                                                            *
*    MM     MM    YY     YY          CCCCCCC   RRRRR     OOOOOOO   NN    N   *
*    M M   M M     YY   YY           C         R    R    O     O   N N   N   *
*    M  M M  M       Y Y             C         R    R    O     O   N  N  N   *
*    M   M   M        Y              C         RRRRR     O     O   N  N  N   *
*    M       M        Y              C         R   R     O     O   N   N N   *
*    M       M        Y              C         R    R    O     O   N   N N   *
*    M       M        Y              CCCCCCC   R     R   OOOOOOO   N    NN   *
*    M       M        Y                                                      *
******************************************************************************
STRHEADR;

		return $usageMessage;
	}

	/**
	 * Echo's a disclaimer to the end user if running in hypothetical mode
	 *
	 * @param $boolActuallyUpdateDatabase
	 * @return void
	 */
	public static function runInHypotheticalMode($boolActuallyUpdateDatabase) {
		if (!$boolActuallyUpdateDatabase) {
			echo "\nNote Regarding Hypothetical Mode\n";
			echo "--------------------------------\n";
			echo 'The script is running in hypothetical mode.  All database changes referred to';
			echo ' in the output below will be rolled back before the script completes.  The changes';
			echo " will also be rolled back should the script be interrupted part way through.\n";
		}
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment