Archive for the ‘Article’ Category

Article | No Comments | October 17th, 2008

If you’re including files based on user input you must think carefully about the security implications of this. When I say user input, I mean any value coming from outside your PHP script that is used in the formation of a file path. This could be as simple as a user clicking a link containing URL-parameter whose value is a predefined path to a file you wish to include.

Let’s first examine some code that accepts the name of a file and includes the contents of that file in a PHP page. The file named in the URL-parameters “body” is specified as an include.

<html>
<head>
<title>Blah</title>
<table border=1>
<tr>
<td width=100>
<a href=index.php?body=news.html>Test</a>

<td><? include("$body");?>
</table>

Be aware this is a security risk. Suppose a mischievous user enters a link into their browser with something like this:

index.php?page=../../../etc/passwd

Allowing directory navigation symbols into input exposes your host system’s password file and allow anyone on the web to read it. Moreover, it can expose any document on your web site. In this article, we will look at various solutions for allowing material to be included while at the same time closing this loophole.

You might think that you could just hardcode a directory or folder name into the path like this:

<a href=index.php?body=news.html>Test</a>
<td><? include('folder/'.$body);?>

But this does not work because directory navigation symbols (“../” and “./” can always be included in the path to get out of this directory. A malicious user can navigate anywhere, even out of the web tree.

Validating Include Paths


The solution is, instead of just blindly including the file, we check for any directory navigation symbols in the submitted value before the file is included. I will show you how to create a function we can run the value through before executing the include.

The following script demonstrates how it works. You will need a text file to experiment with:

kumquat.txt

<h3>Kumquat</h3>

<p>Any of several trees or shrubs of the genus Fortunella bearing small orange-colored edible fruits
with thick sweet-flavored skin and sour pulp.
</p>

And this PHP page: safeinc.php:

<?php

print "<h2>Testing Safe Include</h2>";

function is_safe_file_path($path) {
	if ((!eregi("^..",$path)) && (!eregi("^.",$path)))
	{
		return true;
	} else {
		return false;
	}
}

$include_path = "kumquat.txt";

?>
Including: <?php print $include_path; ?>
<div class="note" style="background: #eeeeee; color: #333333; border: 1px dashed;
   padding: 4px; font: 12px Arial">
<?php

if(is_safe_file_path($include_path))
{
	include($include_path);
}

?>

</div>

Put both files in the same directory and run safeinc.php to see the result. You can try entering “../” or “./” or “../../” or “../kumquat.txt” etc. into the $include_path to see it reject those paths with unsafe path symbols.

Be sure to form your full path, such as “fruits/kumquat.txt” before running it through the function and including.

Of course, you can have paths that go deeper in the directory tree like the previous, but not “../fruits/kumquat.txt” because that is what we are trying to prevent: navigating out of the folder we are in.

For this example, I also left out encapsulating the include into a function to make it easier to understand (and code!). Here’s a version of safeinc.php that wraps the include into a function.

safeincfn.php:

<?php

print "<h2>Testing Safe Include</h2>";

function is_safe_file_path($path) {
	if ((!eregi("^..",$path)) && (!eregi("^.",$path)))
	{
		return true;
	} else {
		return false;
	}
}

function safe_include($path) {
	if(is_safe_file_path($path))
	{
		include($path);
		return true; // indicate success
	}
}

$include_path = "kumquat.txt";

?>
Including: <?php print $include_path; ?>
<div class="note" style="background: #eeeeee; color: #333333; border: 1px dashed; padding: 4px; font: 12px Arial">
<?php

safe_include($include_path);

?>
</div>

Or a more compact function that folds all the features into one:

function safe_include($path) {
if ((!eregi("^..",$path)) && (!eregi("^.",$path)))
	{
		include($path);
		return true; // indicate success
	} else {
		return false;
	}
}

Or with built-in warning and script halt. Silent on success, display message on fail.

function safe_include($path)   {
if ((!eregi("^..",$path)) && (!eregi("^.",$path)))
	{
		include($path);
	} else {
		print "Warning - you do not have permission to access this area.";
		exit;
	}
}

Article | No Comments | October 17th, 2008

Here is a quick way to create a configuration file from the web, used to initially setup an application or that can be expanded to allow users to modify the configuration later.

We will take advantage of the PHP’s ability to include code into an existing script. Instead of creating an initialization file that must be read and parsed, we let PHP do the parsing. The configuration file consists of a series of variable definitions. Our example script could be used to initially setup a database driven web application, such as a message board, etc. It may also be extended to allow updating of application settings at any time.

Our example will contain settings frequently used in PHP applications: values used for connecting with and querying a database and a filesystem path. Here is what the resulting configuration file might look like.

<?php
// Do not edit this file. Generated by admin script.
// Database settings
$db_user = 'me';
$db_pass = 'noneofyourbizness';
$db_host = 'localhost';
$db_name = 'mydb';
$db_table = 'mystuff';
// Paths
$base_path = 'home/users/me/mystuff';
?>

Our goal is to accept values from a web form and write changes to a configuration file.

For example, if you wanted to create a setup script for a database driven web application, users would go to a form page with the following inputs:

db_user     Database user name.
db_pass     Database password.
db_host     Database host name.
db_name     Database name.
db_table    Name of table application uses.
base_path   Path to folder containing files
            of interest to the application.

Writing the Configuration File


function write_config()

{

// For every setting, add a global variable

global $db_user,$db_pass,$db_host,$db_name,$db_table;

global $base_path;

// Prepare settings

// Using single quotes is easier because you don't have to escape them. For every setting, add a line.

$settings = "<?php\n";

$settings .= "// Do not edit this file. Generated by admin script.\n";

$settings .= "// Database settings\n";

$settings .= "\$db_user = '$db_user';\n";

$settings .= "\$db_pass = '$db_pass';\n";

$settings .= "\$db_host = '$db_host';\n";

$settings .= "\$db_name = '$db_name';\n";

$settings .= "\$db_table = '$db_table';\n";

$settings .= "// Paths\n";

$settings .= "\$base_path = '$base_path';\n";

$settings .= "?>\n";

// Write out new initialization file

$fd = fopen( '/somepath/config.php', "w" )

or die ("Cannot create configuration file.");

fwrite( $fd, $settings );

fclose( $fd );

}

Note Because I made this a function, we need to include any variables we are going to write into the configuration as global variables or function parameters. I chose to make all variables used by the configuration file globals. They will likely be global to the whole application anyway (unless you’re using classes).

Getting Input from the User


To use this all you need is a form with inputs matching the variable names. The values get submitted and then written out as variables in the configuration file. When the configuration file is loaded by the application, the submitted values are available.

I’ve designed a form for use with the setup script.

<form action="setup.php" method="POST">

<style>
td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: x-small; color: #333333 }
  .formhed { background-color: #cccccc; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: x-small; color: #333333 }
  .intputhed { background-color: #eeeeee; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: x-small; color: #333333 }
</style>

<table cellspacing="2" cellpadding="5">
   <tr class=intputhed>
      <td colspan="2" class=formhed>Setup </td>
   </tr>

   <tr class=intputhed>
      <td>Database User Name</td>
      <td><input type="Text" name="db_user" size="24" value="<?php echo $db_user; ?>"></td>
   </tr>

   <tr class=intputhed>
      <td>Database Password</td>
      <td><input type="password" name="db_pass" size="24" value="<?php echo $db_pass; ?>"></td>
   </tr>

   <tr class=intputhed>
      <td>Database Host</td>
      <td><input type="Text" name="db_host" size="24" value="<?php echo $db_host; ?>"></td>
   </tr>

   <tr class=intputhed>
      <td>Database Name</td>
      <td><input type="text" maxlength=60 size="24" name="db_name" value="<?php echo $db_name; ?>"> </td>
   </tr>

   <tr class=intputhed>
	<td>Database Table</td>
	<td><input type="text" maxlength=60 size="24" name="db_table" value="<?php echo db_table; ?>"> </td>
   </tr>

   <tr class=intputhed>
	<td>Full Path</td>
	<td><input type="text" maxlength=60 size="24" name="base_path" value="<?php echo $base_path; ?>"> </td>
   </tr>

   <tr class=intputhed>
	<td colspan="2">
	   <input type="Hidden" name="action" value="update_config">
	   <input type="Submit" value="Next >>"> </td>
   </tr>

</table>

</form>

Security Concerns


Anytime you create or write an operating system file from a web based form and script combination, you must consider how secure it is. There is potential for abuse if users can write files all over the place.

First, you should be sure that the path to the configuration file is hardwired into the script. Never let the user define this path directly from the form input. If you do have a need for the user to select from a number of configuration files, select them from an array of allowed paths. The form should only allow the users to indicate which path they are selecting, for example, by an option number obtained from a drop down menu.

Second, you should be sure the configuration file is not writeable by anyone on the net. It should be writeable only by the owner and any script granted access to the filesystem.

If PHP is not running under your user id, then you will have to have the user manually make the file world writeable until the new file is written out, then have them manually change the permissions so it not world writeable.

Third, I suggest user input be run through a function that strips any HTML tags, SSI includes, Unix system characters or commands before writing the configuration out. You may need to allow some of these given the purpose of the input. For example, if the input is for a snippet of HTML (you still want to be careful, because submitting a bit of JavaScript could wreak havoc if displayed in your browser and a snippet of PHP code could also do harm).

Article | No Comments | October 17th, 2008

How to copy the contents of one table to another using SQL is not immediately obvious.

Create and Populate Table

create table mytablecopy select * from originaltable

Copying a Table into an Existing Table

insert into originaltable select * from mytablecopy

Sub-Selects

MySQL doesn’t yet support sub-selects. For example you cannot do this:

    select * from things where thing.userid not   in (select userid from
otherthings)

Article | No Comments | October 17th, 2008

This quick tutorial illustrates using functions that generate HTML to create start and end table rows. This is a technique, creating three functions, two to create the start (including header row) and end table sections and a middle section generated by looping construct that outputs HTML table rows filled in with data. This is great for standardizing and gaining control over the HTML tables your applications generate. A lot better than a bunch pages with code randomly strewn through HTML tables. It keeps the code in one place, it keeps you pages simple by only having to drop a function call into it where you want the table. YOu can take advantage of the function to set the appearance or names of table headings.

Tip It’s a good practice to name all functions that generate HTML starting with “make_”. This makes it clear which functions output HTML.

	function make_users_table_start() {
	$content = '<table width="100%" border="0" cellspacing="1"
	 cellpadding="2" bgcolor="#000000">
<tr bgcolor="#e7e7d6">
<td><font face="Verdana,Arial,sans-serif"
 size="-1"><strong>User Name
 </strong></font>

 </td><td><font
 face="Verdana,Arial,sans-serif" size="-1">
 <strong>Name</strong></font></td><
 td><font face="Verdana,Arial,sans-serif" size="-1">
 <strong>Email Address</strong></font></td>

 <td><font face="Verdana,Arial,sans-serif" size="-1">
 <strong>Member Group</strong></font></td><td>
 <font face="Verdana,Arial,sans-serif" size="-1">
 <strong>Member Since</strong></font>

 </td>';
	return $content;
}

function get_alt_row_color($i,$light_color="#ffffff",$dark_color="#E6EFFD") {
if ($i % 2 == 0) {
		return $light_color;
	} else {
		return $dark_color;
	}
}

The get_alt_row_color() function adds a little eye candy and readability to the table.

function make_users_table_end() {
$content = '</table>';
return $content;
}

function browse_users() {
$sql = "SELECT
 user_id, user_pass, personal_name, family_name,
 user_group, email_address,
 DATE_FORMAT(date_created, $date_format) AS member_since
  FROM user ORDER BY user_id DESC";
$result=mysql_query($sql);
if (!$result) {
    print mysql_error()." ERROR - browse query failed.";
}

while($row = mysql_fetch_array($result)) {
		$content .= '<tr bgcolor="'. get_alt_row_color($line_count). '">';
		$content .= '<td class="normalprint"><font
		 face="Verdana,Arial,sans-serif" size="-1">' ."\n";
		$content .= '<a href="user-detail.php'. make_sid()
		 .'&user_id='. $row[user_id] .'">'. $row[user_id]
		  .'</a></font></td>

		 <td class="normalprint"><font face="Verdana,Arial,sans-serif"
		  size="-1">'. $row[personal_name] .' '. $row[family_name];
		$content .= '<td class="normalprint"><font
		 face="Verdana,Arial,sans-serif" size="-1">' ."\n";
		$content .= $row[email_address];
		$content .= "</font>\n\t</td>\n";
		$content .= '<td class="normalprint"><font
		 face="Verdana,Arial,sans-serif" size="-1">' ."\n";
		$content .= $user_group[$row[user_group]];
		$content .= "</font>\n\t</td>\n";
		$content .= '<td class="normalprint"><font
		 face="Verdana,Arial,sans-serif" size="-1">'.
		  $row[member_since] ."</td>\n";
		$content .= "</font>\n\t</td>\n</tr>\n";
		$line_count++;
	}
}

Adding the output of this query is as easy as adding three funtion calls to a PHP page. It doesn’t even matter hwat directory it’s in.

print make_users_table_start();

print browse_users($start_list);

print make_users_table_end();

Tip Don’t forget the print statement! Otherwise, you’ll get nothing.

Or you can create a wrapper function for all of them. Place it anywhere on any PHP page and you’re ready to go!

function browse_box() {

print make_users_table_start();

print browse_users($start_list);

print make_users_table_end();
}

Article | No Comments | October 17th, 2008

This is a brief guide to installing PHP (a server-side web page scripting language) and MySQL (a popular relational database system) on the NT platform.

Important Before installing PHP and MySQL on Windows NT, you must logon as a user with administrator privileges.

PHP

You need to download two files to install PHP for Windows (and you must have some way of unzipping “zip” compressed files (e.g. Winzip).

  1. Go to http://www.php.net
  2. (Select a mirror site close to you)
  3. Click on the ‘Downloads’ link.
  4. Download the zip package file (currently “PHP 4.1.1-Win32.zip”)
  5. Also download the installer (currently “PHP 4.1.1-installer.exe”)

Unzip the zip file to a folder (e.g. C:php) put the “PHP 4.1.1-installer.exe” file in the same folder and double click it to run it. It will install it just like a normal piece of Windows software – Just follow the onscreen instructions.

After installing you need to configure the “php.ini” file which needs to be saved in C:Windows. There will be an example one given to you in your php installation folder which is ideal to use. (It only needs changing if you install it in a different folder or want non-default options).

Note You can see some (old) instructions for installing PHP on Windows at: http://www.e-gineer.com/instructions/install-php3xx-for-iis4x-on-windowsnt.phtml

MySQL

Go to http://www.mysql.com and click on the ‘Downloads’ link. Go for the stable version, (currently v3.23.47). Scroll down to the Windows Downloads section and click on the link to download the zip file. (As said, currently: ‘mysql-3.23.47-win.zip’)Extract this to a folder (e.g. C:MySQL) and then double click on the “Setup.exe” file to install.

That’s it!

Make sure the web server is running and your PHP files are in the web publishing directory for them to work as intended. To run the MySQL server, create a shortcut to the “winmysqladmin.exe” in the folder C:mysqlbin (this may get generated automatically but on my installation it didn’t!) and place it in your start menu or on your desktop. This will start and stop your MySQL server when required.

Tip If you are new to MySQL then a helpful tool that provides a Windows graphical front-end is MySQL Front available from http://www.mysqlfront.de

Article | No Comments | October 17th, 2008

Note This is a reprint of an article on Perl text normalization. It refers to Perl code not PHP code. Hope you find it useful.

Text is text, right? Not necessarily true when you are concerned with writing web-based applications. Because the net is composed of many different kinds of computers, a CGI program cannot just assume a one size fits all attitude. It must expect text will be coming from a variety of sources and formats. There are three main formats of text in use today, differing only in the way the end of line or newline is marked. The PC uses a carriage-return linefeed pair (CRLF) to mark the newline. Mac newlines are just a single carriage-return (CR). And Unix/Linux systems employ a single linefeed. If you ever wonder why your files appear to “shrink” when uploaded to a web server, it’s because of those unnecessary characters the PC text format adds (typically conversion takes place when files are uploaded in ASCII mode by FTP). But enough history, lets look at what a CGI-forms interface sees when it receives input from the user.

Attaining a State of Normalcy

I think it’s good practice (as a friendly Perl expert once taught me) to normalize all text before attempting to process it. If you try to operate on text expecting each line to end with a linefeed but the lines actually end with a carriage return linefeed pair, the results may be unexpected. By normalizing text, you won’t have to deal with several different kinds of linefeed all mixed together. The main parts of your CGI program will only have to understand one format, not three. Since you can’t predict what browser or operating system and hence format of text, it’s best to normalize all text input to a common format. One user may be on a Unix system, then another may come along on a Mac—if you just post their data to a text file it will end up scrambled by the mix of different newlines.

Note: For this reason, the W3C has reccommended in the HTML4.0 specification that all browsers normalize TEXTAREA (and I suppose TEXT input content) to CRLF format). But it will be a long time before you can rely on it. I see Netscape 1.1 occassionally in my logs to this day [at the time of writing, 1996-97].

Because Unix/Linux is the operating system widely running web servers, this example will normalize to the Unix single linefeed. But you can normalize to any format, such as to the PC format if you are running Perl on Windows NT or the Mac format if your web server runs under Mac OS.

#--------------#
# Normalize textarea
# input to Unix newline format.
# Input:
# $text: string to be normalized
# Note: HTML4.0
# recommends browsers normalize
# their textarea
# content to CRLF format. This, I hope
# will eventually make
this step unnecessary.
#---------------#

# Convert PC newline (CRLF)
# to Unix newline format (LF)

$text =~ s/\r\n/\n/g;

# Convert Mac newline (CR)
# to Unix newline format (LF)

$text =~ s/\r/\n/g;

Other Text Tricks

Now that you have the text input from the form normalized, what are other considerations? A line that contains only spaces or a few tab characters is not really useful–the extra characters are unnecessary baggage. So you can remove them.

# remove spaces and tabs from blank lines
$text =~ s/\n[ \t]+/\n/g;

If you have no need to retain format of the original plain text, then you can remove leading and trailing line breaks. This is fine if the text is only intended for inclusion in an HTML document (except for PRE, which retains formatting), say in a paragraph element.

# remove leading and trailing line breaks
$text =~ s/^\n+|\n+$//g;

Returning From Normal

You can also convert text normalized to Unix format back into PC or Mac formats.

# Translate to target line format.
#----------------------------------#

#Change to Mac format
# LF to CR
$text =~ s/\n/\r/g;
# Translate to target line format.
#-----------------------------------#

# Put Unix into PC format
# LF to CRLF
$text =~ s/\n/\r\n/g;

Does Normalizing Matter?

Yes. Because as long as Mac browsers submit Mac format text and PC browsers submit DOS format text, you will need to normalize TEXTAREA input. Of course, you do not have to normalize INPUT text because it is not a multi-line input. Perl always works with text in its native format, so must normalize. It’s best to normalize text to the format native to the server hosting the website. Unless you have a special need, such as writing in PC format for download and editing on PC’s.

PHP and Normalization

I’m uncertain whether other languages accept and process text in native format. I’ve not really had any trouble nor have I had to normalize TEXTAREA input in PHP. Maybe it normalizes input or most browsers today are submitting normalized text [in 2002].

Article | No Comments | October 17th, 2008

nswers to some frequently asked questions and solutions to some common PHP programming tasks. (With a few comments on how PHP syntax relates to Perl).

How Do I Tell if PHP is Working?

You can check to see if PHP is running and correctly configured by making a simple one line script:

	<?php print phpinfo(); ?>

I usually call this phpinfo.php and upload it to the public web server directory, then point my browser to it. It displays the configuration of PHP in great detail.

How do I make a PHP page?

This may sound a little silly, but often people are confused about how a HTML page differs from a PHP page. A PHP page is just a regular plain text file like any other web page except that it has a “php file extension,” usually “php” and meaning that this page will be run through PHP before going to the browser. Any PHP program code in the page will be executed. Any HTML will be passed on to the browser untouched. Making a PHP enabled web page is as easy as opening a new file in your favorite HTML or text editor. The editor can be as simple as Notepad or as complex as HomeSite. The choice is yours.

Type

	<?php
	   print "Hello World";
	?>

Save this file with the php file extension, such as “hello.php” (without the quotes) to your hard disk.

Upload the file to your PHP enabled web server. It must be in your web server’s document root or a directory below it (this is usually a directory called public_html or www or something similar).

Load the page in your web browser, the URL should look something like

	http://www.example-domain.com/hello.php

Remember, that you may have to change the extension to suit how the web server is configured. If you’re using PHP3, you may have to give your file the php3 extension instead of the standard php.

How do I quote strings?

PHP syntax takes some inspiration from the popular Perl programming language. In Perl, variable substitution is done in a double quotish context but not in a single quotish context. PHP follows this rule. To see how this works, run

$test = "Test";
print "Test Double Quotish Context: $test
";
print 'Test Single Quotish Context: $test
';

By single quote context, I mean any characters enclosed in single quotes. By double quote context, I mean any string of characters enclosed by double quotes. Variable substitution refers to replacing the value of a variable where that variable name appears in the string.

The type of quotes you use to enclose a string literal can also affect the use of character sequences used to escape certain special characters:

print '<pre>';
print "This is a double quote context\n";
print 'This is a single Quote context\n';
print '</pre>';

When printed to the browser, the second line will contain “\n” instead of a newline.

This is a double quote context
This is a single Quote context\n

Not understanding how quotes work can lead to frustration and mysterious errors.

For example take a look at the code below.

$dir='arcade';
$handle=opendir('/usr/www/users/userid/vdomain/test/games/$dir/');

The opendir() function gives mysterious errors until you stop looking at the path and notice the single quotes. This literally evaluates to

/usr/www/users/userid/vdomain/test/games/$dir/

which will give errors, instead of

/usr/www/users/userid/vdomain/test/games/arcade/

where the $dir variable’s value gets properly substituted into the directory path string.

For this to work correctly use double quotes instead instead of single quotes:

$dir='arcade';
$handle=opendir("/usr/www/users/userid/vdomain/test/games/$dir/");

PHP also allows string literals to be broken over multiple lines. Very useful for organizing or structuring HTML tags, such as for indenting tags used in TABLEs. Notice that spaces are significant in multi-line quoted material. (This is a little easier than Perl, but lacks that language’s mechanism for specifying the start and end quote symbols that makes dropping HTML blocks into code a snap).

print "
<p>
This is a multiple
 line quote
</p>

Printing to Web Pages and First Steps to Functions


How do I include PHP values in a HTML tag?

Although it may seem a strange question, beginners often wonder how they can output values as attributes into HTML tags, not just between them. Maybe this is because PHP code sections look like HTML tags and they are unaware that you can intermingle HTML and PHP code anywhere on the page. This includes the “inside” portion of tags. Although PHP code looks like a tag itself, it is not restricted to any particular place in HTML. A web page is just on long stream of text where PHP can itself generate HTML codes. Values from variables can be inserted into HTML tags by printing them where you want them to appear in the HTML code.

An easy way to include a <?php … ?> tag in an HTML tag (<body>) is shown here.

<body bgcolor="<?php print $bgcolor; ?>" text="<?php print $textcolor; ?>">

Mixing code directly into HTML tag attributes can easily become confusing and error prone. The following code is cleaner and more controlled way of specifying values in a PHP generated tag:

<?php
$bgcolor = "#fffff";
$text = "#000000";
$link = "#0000FF";
print "<body bgcolor=$bgcolor text=$text link=$link>";
?>

In this example, I make it easier on the programmer by not quoting attribute values. This is not the best practice. The HTML specification requires that certain attribute values be quoted. It’s usually a good idea to always quote, but of course stray quotes can easily bring down your program. Try this:

<?php
$bgcolor = "#fffff";
$text = "#000000";
$link = "#0000FF";
print '<body bgcolor="'. $bgcolor .'" text="'. $text .'" link="'. $link .'">';
?>

I use the single quote for static text portions of the string where I do not need variable substitution. Values are included using the concatenation operator, which combines string values into a single string. This may be a little slower but it’s a lot clearer. It’s also easy to add or insert another string into the output should another attribute be needed.

Using a Function to Make HTML

Instead of writing the same code over and over for each tag, you could make this into a function.

<?php
function html_body($bgcolor,$text,$link ) {
	$bgcolor = "#fffff";
	$text = "#000000";
	$link = "#0000FF";
	print '<body bgcolor="'. $bgcolor .'" text="'. $text .'" link="'. $link .'">';
}
?>

The function definition can be placed in a configuration page included into any pages where you wish to control the body attributes. Instead of the tag you call the function that generates it. You can reuse the code anywhere the tag is needed, thus saving time, effort and errors.

<?php
function make_html_body($bgcolor,$text,$link ) {
	$bgcolor = "#fffff";
	$text = "#000000";
	$link = "#0000FF";
	print '<body bgcolor="'. $bgcolor .'" text="'. $text .'" link="'. $link .'">';
}
?>
<head>
<title>Page With A Dynamic Personality</title>

</head>
<?php print make_html_body("#fffff","#000000","#0000FF"); ?>

<h2>Page With A Dynamic Personality</h2>

To drop the HTML output into our page we need only print the value returned from the make_html_whatever() function.

To see how it works, copy the code into a PHP page and run it. The function definition does not have to be literally in the page as I show here, but can be included from another file.

However, sometimes having the function return its value by printing directly to the browser can cause problems. Another programmer may want to use the same function on a page that handles cookies. The print statement may interfere with this. Can we create a function that can be used in any context, with the programmer deciding how to use the output? Yes, here’s how:

<?php
function make_html_body($bgcolor,$text,$link )
{
	$bgcolor = "#fffff";
	$text = "#000000";
	$link = "#0000FF";
	$content = '<body bgcolor="'. $bgcolor;
	$content .= '" text="'. $text;
	$content .= '" link="'. $link .'">';
	return $content;
}
?>

Instead of printing the output directly to the web browser through PHP’s built-in output stream, we return it as the value of the function. I’ve broken the sting into three separate concatenations (using the .= operator that both puts the strings together and assigns the new value to the variable), just to show you how it can be done. Often you’ll build up the output value from a series of string operations. You don’t have to specify a return value, because PHP returns the last value assigned in a function. But it’s good documentation practice to explicitly show the return statement. If you’re concerned about efficiency, just comment out the return statement.

You’ve now just learned a very important principle in programming. It’s called modularity and shows you the advantages of reusing code, containing code in packages that are easy to locate all in one place (the place where you keep all the functions used on your web pages) and write functions that return values, which can be used without regard to their context. You don’t know it, but you are on your way to understanding object oriented programming. But unfortunately, you’re also leaving behind the easy days of just mixing code in with HTML. When it comes to large web applications it’s often better when the script is generating all HTML directly from a series of print statements or a function.

Web Pages With Variable Content


How do I include variable content in a PHP page?

You may have seen on quite a few sites when they link to a file they just link to “www.example-domain.com/?page=pic.html&pic=jane-austin.jpg” or such. The idea is to link to a PHP page that includes the selected picture inside a web page without having to make a separate html file for every single picture, for example.

Unless you need to apply a universal template to the pages, you can simply make this a PHP page that accepts and displays variable content. PHP is a preprocessor, which means it automatically reads the page and executes code it finds. Any variables from a link are automatically brought into existence.

For this example, If you do not need templates to be varied by the image, just create a PHP page with whatever HTML you want on it, then include:

	<img src=<?php print $pic; ?> alt="">

Unfortunately, PHP does not have an easy way of getting the height and width of an image so they can be added to the IMG tag. (However, an imaginative programmer might create snippets of JavaScript to peek at the height and width and document.write() them out to the IMG tag. But I’ll leave that for the future.

If the page is index.php, you can call it with only the domain name

	domain/?$pic=victorian-lady.jpg

or by specifying a directory.

	domain/directory/?$pic=victorian-lady.jpg

Basics of Accessing the CGI Enviroment


Getting Information About Web Browser Capabilities

I am not an advocate of serving different content based on which web browser the user is visiting. But sometimes it is unavoidable. Or you may want to collect information to tune your web pages to the particular capabilities of your site’s audience. When a browser requests a page from a web server a number of environment variables are set. These are fairly standardized, although some web servers and software (such as PHP) may create or set new one not in a standard RFC paper. Software used to browse the web is called a “user agent” because it acts as an agent for the user, retrieving and displaying requested files. The browser is the client and the web server is, naturally, the server in this relationship.

$env["HTTP_USER_AGENT"] = getenv("HTTP_USER_AGENT");
$env["HTTP_ACCEPT"] = getenv("HTTP_ACCEPT");

Peeking Into the HTTP Environment

You may be curious about what environment variables are available. Here is a small script to display some of the most common and standard HTTP environment variables.

$env["REMOTE_ADDR"] = getenv("REMOTE_ADDR");
$env["SERVER_SOFTWARE"] = getenv("SERVER_SOFTWARE");
$env["SERVER_NAME"] = getenv("SERVER_NAME");
$env["GATEWAY_INTERFACE"] = getenv("GATEWAY_INTERFACE");
$env["SERVER_PROTOCOL"] = getenv("SERVER_PROTOCOL");
$env["SERVER_PORT"] = getenv("SERVER_PORT");
$env["REQUEST_METHOD"] = getenv("REQUEST_METHOD");
$env["PATH_INFO"] = getenv("PATH_INFO");
$env["PATH_TRANSLATED"] = getenv("PATH_TRANSLATED");
$env["DOCUMENT_ROOT"] = getenv("DOCUMENT_ROOT");
$env["SCRIPT_NAME"] = getenv("SCRIPT_NAME");
$env["QUERY_STRING"] = getenv("QUERY_STRING");
$env["REMOTE_HOST"] = getenv("REMOTE_HOST");
$env["AUTH_TYPE"] = getenv("AUTH_TYPE");
$env["REMOTE_USER"] = getenv("REMOTE_USER");
$env["REMOTE_IDENT"] = getenv("REMOTE_IDENT");
$env["CONTENT_TYPE"] = getenv("CONTENT_TYPE");
$env["CONTENT_LENGTH"] = getenv("CONTENT_LENGTH");
$env["HTTP_ACCEPT"] = getenv("HTTP_ACCEPT");
$env["HTTP_HOST"] = getenv("HTTP_HOST");
$env["HTTP_USER_AGENT"] = getenv("HTTP_USER_AGENT");
$env["HTTP_REFERER"] = getenv("HTTP_REFERER");
$env["HTTP_REFERRER"] = getenv("HTTP_REFFERER");

print "<table border=1 cellspacing=0 cellpadding=2>";
print "<caption>Environment Variables</caption>";
while ( list( $key, $val ) = each( $env ) ) {

	print "<tr><td bgcolor=#CCCCCC
	><b>$key</b></td><td bgcolor=#EEEEEE><i>$val</i></td></tr>";
}
print "</table>";
}

Why do I use getenv() instead of looking into the GLOBALS array for these values? Because anyone can spoof those values in the globals array. We cannot trust these same values when they come from the GLOBALS array, but getenv() retrieves the values directly from the environment. Many of these values are important to maintaining security on the web, such as REMOTE_HOST (for access to user’s IP address) or PATH_INFO (sometimes carrying crucial information to scripts).

You may notice a few strange things about environment variables. For historical reasons, both the misspelled REFERRER (the misspelled version) and REFERRER are available. Handling referring URLs can be tricky because some older web servers still use the misspelled version, so you should check for a value in both. Sometimes variables become obsolete. For example, the HTTP_FROM variable generally returns nothing in most modern browsers. Originally it was for returning the user’s email address in order to make filling out forms easy, but was dropped for privacy and anti-spam reasons.

How Can I See All the CGI Variables?

When debugging, you may want to see all the variables posted by a form or associated with a link (variables and values coming in from the CGI environment). Sometimes there may be an extra variable you had not thought was being submitted or you might be lacking a needed variable. HTTP_POST VARS holds all the variables coming from an HTML form submission.

// Dump POST Variables

	if($HTTP_POST_VARS)
	{
		print "<table border=1 cellspacing=0 cellpadding=2>";
		print "<caption>Values submitted via POST method</caption>";
		while ( list( $key, $val ) = each( $HTTP_POST_VARS ) )
		{
			print "<tr><td bgcolor=#CCCCCC><b>$key</b></td>

<td bgcolor=#EEEEEE><i>$val</i></td></tr>";
		}
		print "</table>";
	}

To look at the variables coming from a scripted page that is activated by a link, check HTTP_GET_VARS.

// Dump GET Variables

print "<table border=1 cellspacing=0 cellpadding=2>";
print "<caption>Values submitted via GET method</caption>";
while ( list( $key, $val ) = each( $HTTP_GET_VARS ) ) {

	print "<tr><td bgcolor=#CCCCCC><b>$key</b></td>

	<td bgcolor=#EEEEEE><i>$val</i></td></tr>";
}
print "</table>";

Both HTTP_GET_VARS and HTTP_POST_VARS are variables introduced into the CGI environment by PHP itself (they are arrays created when a PHP page executes). These are not part of the standard environment if PHP were not running (as far as I know).