PHP Power Pointers
The 10% you need -- for 90% of your work
Introduction
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
- Integrated Database Connectivity
- Session Management
- Object Orientation
- Regex
- Quick Development
Impossible Deadlines
Assignment:
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.
Assignment:
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.
Parsing
Assignment:
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 :-)
-
Open input (disk report) and output files.
-
while(<INPUT_REPORT>) #pseudo code, not
exact
-
Read next line
-
Use regular expression to filter out JCL.
-
Use regular expression to deduce whether line is new record, or just word
wrap
-
On new record
-
finish old record
-
write old record
-
zero totals
-
start new record
-
Using regular expressions, parse the data from the prose and update all
totals.
-
Close input and output files.
Quick and Dirty Reports, and Error Checking
Assignment:
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>;
chomp($name);
return($name);
}
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:
my($name);
$name = <STDIN>;
[ Troubleshooters.com
| Code Corner | Email
Steve Litt ]
Copyright
(C)1998 by Steve Litt --
Legal