gather your junk into a toolbox

by Derek Sivers

Man I love making toolboxes.

Combining all your junky functions into organized tools is like going through your messy garage, putting what you need into boxes, and throwing out the rest.

Here's how it usually goes:

1. I write some quick script to get the job done ASAP.
2. At the top of that script I write a little function I needed at that moment.
3. Next time I'm writing something quick, I remember that little function, and I go cut-n-paste it into my new script.
4. A year later that little function is used in 20 different places.
and then...
5. Some day I finally put that function and a couple related functions into a tool-file, to be included whenever needed. While doing so, I realize some way it could have been written better all along, and I clean it up.

Here's one:

------ FILENAME: ShellTools.php

/* SHELL UTILITITIES for interactive PHP command-line scripts */

function stdin($length = 255)
  $fr = fopen('php://stdin', 'r');
  $input = fgets($fr, $length);
  $input = rtrim($input);
  return $input;

function stdout($message)
  $fp = fopen('php://stdout', 'w');
  fwrite($fp, $message);

function answer($question, $length=255)
  stdout($question . ' ');
  return stdin($length);

function arg($num, $description = 'something', $required = false)
  $response = (isset($_SERVER['argv'][$num])) ? $_SERVER['argv'][$num] : '';
  if(($required != false) && ($response == ''))
    die("needs $description as command-line argument #$num\n");
  return $response;


What does this ShellTools.php solve?

Most of my command-line PHP shell scripts needed some kind of interaction.
In FreeBSD, for some reason, PHP's "print" or "echo" commands wouldn't display anything on the command-line until after the script was done! No good for interaction. So I had to use the stdout.
Now it's as easy as using:
require 'ShellTools.php';

Also, I was often using variables passed into my script on the command-line, like this:
php some_script.php FileName.txt
At the top of all those scripts, I was always writing something that would be looking in the command line for something, and giving an error message if it wasn't there.
Now it's as easy as this:
require 'ShellTools.php';
# true means it's required. die without it:
$filename = arg(1, 'filename', true);

If I forget to include the filename, it nicely tells me:
"needs filename as command-line argument #1"

Lastly, for real shell-script interaction, my scripts ask questions.
This is where all that stdout and stdin comes in handy:
require 'ShellTools.php';
$name = answer('Your name?');
$city = answer('What city?');
stdout("Bake me a cake, $name - I'm coming to $city!");

I know this stuff is super-basic for many of you, but it's only because I used a super-basic example here. But the last two days I just spent about 20 hours going through months of old messy duplicated code that really runs a significant part of our daily operations, and extracted ALL the common stuff into just a couple shared classes.

What a difference! These scripts are so much cleaner now! So much easier to manage and change in the future. So much easier to find a way to improve a single function and have ALL scripts improved by it.

Know what I'm sayin'?


2004-08-07 01:40:12
Did you try using flush()?
2004-08-07 07:42:46
Did you try using flush()?
Yep. Still wouldn't output until the script was through.