Troubleshooters.Com and Code Corner Present

PHP Power Pointers
The 10% you need -- for 90% of your work

Copyright (C) 2000-2002 by Steve Litt
Steve Litt is the author of Troubleshooting Techniques of the Successful TechnologistRapid Learning: Secret Weapon of the Successful Technologist, and Samba Unleashed.


About PHP


Snippets and Tips

  • Introduction

  • PHP's Outstanding Features

  • PHP Hints and Landmines

  • A Test to See if You Write Sloppy PHP Software



    PHP is a computer language built from the ground up for web applications and database connectivity. It has a clean syntax you'll recognize if you've programmed in C, C++, Python, or any nice and readable language. PHP is not particularly difficult, especially considering what it's used for (web apps).

    PHP version 4 even has a feature called "sessions" that compensates for the statelessness of the http protocol.

    PHP is fairly portable, running on Linux, most unices, and Windows. Although it's an interpreter and therefore not fast in and of itself, it has various performance and caching features making PHP built web apps lightning fast from the user perspective.

    PHP can be developed in a simple editor such as Vim. It has plain text source files, but those source files cannot be viewed as source code on a properly configured server, enabling some degree of security by obscurity (database passwords and the like).

    PHP has no GUI user interface. It's usually used to output HTML, thereby making most PHP apps graphical (in a browser). Note however that PHP can also be used for console apps.

    PHP's Outstanding Features

    Impossible Deadlines


    We need to import each of our hundreds of individual user databases into our new corporate databases. We need this done in 48 hours.

    PERL Solution:

    Subroutine 1:

    Walk the directory tree containing each individual database. For each directory, it calls (via system()) a simple database utility or program to export each table to delimited text, then copies the delimited text records to single files at the root of the directory.

    Subroutine 2:

    Walk through the concatenated delimited files, changing field lengths and positions to match those of the final destination, and changing delimiters to those used by the new database's import utility. Also checks for any errors detectable at this point. Note that dups can be detected using sort() and a simple control-break algorithm.

    Subroutine 3:

    FTP or otherwise transfer the files to the location required by the new database's import utility.

    Batch File and Shellscript Substitutes

    Any time a batch file or shellscript grows beyond 30 statements, or calls other batch files or shellscripts, or contains moderate to complex program flow logic, calls GREP or a text editor, or requires parsing of files, use PERL.


    Our nightly network backups intermittently fail. Some nights they fail because the corporate transfer program is still running. Corporate transfer program runs anywhere between 10 minutes on an easy night to 4 hours at the end of the month. We could always set our backup to go off 4 hours after starting the corporate transfer program, but many nights the /wange directory has so many files that if we set off the backup that late it would fail when users get on the system at 7am. Most of the files in the /wange directory are .wan files, which aren't absolutely critical but should be backed up if possible. The rest of the files in /wange must be backed up.

    PERL Solution:

    Subroutine 1:

    Loop, checking whether the corporate transfer program is running, then sleep(10) minutes. Once corporate transfer is found not to be running, return with the current time.

    Subroutine 2:

    Subtract the current time from 7am to determine the time available for backup. Use opendir(), readdir() and closedir() to determine the number of files in a total backup, and the number of .wan files in /wange. Do arithmetic based on average backup time per file to estimate whether the backup can complete before 7am. If so, return TOTAL_BACKUP. If not, subtract the number of .wan files in /wange. If it can back up that by 7am, return EXCLUDE_WANGE. If it still can't back up by 7am, determine whether /wange/*.wan is greater than 15% of the total. If so, return EXCLUDE_WANGE, otherwise return TOTAL_BACKUP.

    Subroutine 3:

    Create a backup inclusion file that, depending on the return of subroutine 2, excludes or includes /wange/*.wan.

    Subroutine 4:

    Append to a log file. Record a timestamp and the backup command you're about to invoke. Then start the backup with PERL's system() command. When the backup finishes, write a timestamp and the return code of the backup program to the log file.



    We need to get sales data out of the mainframe and into Excel spreadsheets (I know, bad idea, but management demands it). The program is 20 years old and the mainframe programmers don't know how to give you just the data, so they're giving you a disk image of a report. Don't worry, the report's very intuitive, with word wrapping on the data fields, and data fields separated either by spaces, or sometimes one or more tabs. Oh, and also, spurious JCL commands appear in the output occasionally, so you'll need to filter those out. Don't worry though, you've got a couple days to complete this.

    PERL Solution :-)

    Quick and Dirty Reports, and Error Checking


    We need independent confirmation of the timesheet items and hours reported in the database after import.

    PERL Solution:

    Subroutine 1: Deduce totals from individual timesheet files:

    Use opendir(), readdir() and closedir() to get names of individual timesheet files. For each file call do_one_file().

    Subroutine do_one_file:

    Open the file, read and parse it obtaining the number of entries and hours. Add those to the "input file totals".

    Subroutine 2: Deduce totals from intermediate file imported by database:

    Open the file, read line by line. Using line counters and regular expressions, parse for new entries and number of hours. Add those to the "input file totals".

    Subroutine 3: Print the results:

    Print the number of entries and hours as reported from the input files, and as reported from the intermediate file. These should match each other, and should match the reports from the database.

    File Conversions

    For a great example of this, see Impossible Deadlines.

    PHP Hints and Landmines

    Weak Type Checking

    It's real cool to be able to read a sting into a variable, then add it to a floating accumulator or query it for greater or less than PI. Weak type checking furthers PHP's purpose of extremely rapid development. But it can push a reproducible compile time error into an intermittent runtime error, or even worse a wrong result. Just be aware and be careful. If something should be a number, make sure you test it first with a regular expression.

    Variables are Global by Default

    This is one of the two minor criticisms I have of this language (the other concerns object encapsulation issues and is beyond the scope of this document). Check this out:
    sub getName
     $name = <STDIN>;
    sub doLabel
     print "What is your name?==>";
     $name = $getName();
     print "\nWhat is your spouses name?==>";
     $spousename = $getName();
     print "\n";
     print PRINTERDEVICE "Hi. I'm $name.\n";
     print PRINTERDEVICE "My spouse is $spousename.\n";
    This program prints your spouse's name on *both* lines, because $name is global, so the second call to getName() would reset global $name to your spouses name (after you typed it in). To get the program to act as intended, in subroutine getName() replace this:
     $name = <STDIN>;
    with this:
     my($name) = <STDIN>;
    Get in the habit of always using the my() construct on all variables, unless you really want them to be global (and in anything but a quick and dirty throwaway program, globals are asking for trouble).

    NOTE: I've seen rare and intermittent cases where variables constructed as my($varname) = expression act funny in loops. If you find something like that, declare the var on one line and assign it in the next as follows:
     $name = <STDIN>;

     [ | Code Corner | Email Steve Litt ]

    Copyright (C)1998 by Steve Litt -- Legal