Archive for the ‘PHP’ Category.

Strings Are Arrays

I’ve seen a lot of people asking questions about how to find a certain character or sequence inside a string. It is common for people to turn to the library in order to find a function that does this for them, but it is likely the case that the answer is right in front of you. If you need to search thorough a string for whatever reason you can index it as an array. This is the case in a lot of language (PHP, C++, Java).

In a non-type-safe language like PHP people often forget the distinction between primitive types and other constructs. Primitives are often things like int, float, double, long, and char. Strings are generally not a primitive type but an array of chars. Most languages tend to hide this fact from you, because strings are so common it is often more convenient for the programmer to deal with them as if they were not an array of characters.  For example, Java does not support operator overloading; you can only use arithmetic operators (+, -, *) on primitive types, with the exception of strings, which can be appended using the + operator.

So how can you use this fact to your advantage? There are many cases where indexing strings rather than passing them to some function can serve your purpose more efficiently. Here are some examples in PHP:

Ex.
You have several lines of text and you want to count the instances of a particular word. One way to do this would be to use explode to turn the text into an array of words and then count the instances of that word:

$text = 'cat dog chicken cat bird mouse cat lizard';
$words = explode(' ' , $text);
$count = 0;
foreach($words as $word)
{
     if($word == 'cat')
          $count++;
}
echo $count; //should output 3

One function call and one loop, pretty simple right? Maybe not. It is important to remember that function calls may a lot more than they appear. In order to split the string into an array of words, explode must search through the string for a space. If all you need to do is count the number of instances of a word, there is no need to waste time constructing an array of words and then looping through that array. Here is a more efficient way:

$curr = '';
$count = 0;
$searchStr = 'cat';
$text = 'cat dog chicken cat bird mouse cat lizard';
$len = strlen($text);

for($i = 0; $i < $len; $i++)
{
     if($text[$i] == ' ')
     {
          if($curr == $searchStr)
               $count++;

          $curr = '';
     }
     else
          $curr .= $text[$i];
}

The above example contains several more lines of code, but it is important to remember that the number of lines in a program should not be used to measure the performance of a program.

Short Tags

The standard opening tag for php is <?php, however there are two alternative opening tags:

<? – short for <?php
<?= – short for <?php echo

Although they may be convenient, you should never use these tags. The main reason I don’t use them is because short tags can be disabled in the php.ini, so if your application relies on them, it will fail when this feature is disabled. Recently a couple of short tags crept into (by my own mistake) one of my applications and a user reported he was getting a parse error on the last line of code in the file. After looking over the code several times I replaced the short tags with the longer version and all was well.

If you are writing portable applications I recommend that you disable short tags in your own php.ini, so in the case that you place one by mistake (as I did) you will get the error message instead of one of your users reporting it weeks later.

Plugin Compatibility

For various reasons, there are some people out that who have chosen to hang onto older versions of WordPress. Personally I do not recommend this behavior, but as a plugin developer, you might not want to exclude these people from using your plugin. Does this mean you should refrain from using features that were implemented in later versions for the benefit of those who have not upgraded? Absolutely not. One thing you can do, however, is to emulate or implement the newer features your plugin relies on for older versions.

This is actually very simple to do considering you can simply take the code directly from the latest version of WordPress. In order to do this properly, you should make use of PHP’s function_exists function. For example: the function admin_url was not part of WordPress until version 2.6. Normally, if you were to use this function, users with older version of WordPress would not be able to use it. You can, however, define it yourself:

if(!function_exists('admin_url'))
{
	function admin_url($path = '') {
	$url = site_url('wp-admin/', 'admin');

	if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
		$url .= ltrim($path, '/');

	return $url;
	}
}

Notice that the function declaration is inside the if(!function_exists(‘admin_url’)) block. If you simply defined the function as you normally would users with WordPress 2.6+ would receive an error.

Another thing to notice is that the code for admin_url contains a call to a function site_url. This function also was not defined until WordPress 2.6 so you’ll have to do the same thing for this function. For some functions it will be the case that you will have to do this for many functions, and in some cases you won’t be able to do this at all due to more significant differences between older and newer versions.

Here is a file I’m using called compat.php that defines several functions and constants that do not exist in earlier versions of WordPress.

//These functions replicate Wordpress functionality
//Used in cases where a Wordpress function is not
//available (ie a version that does not have that function)
if(!function_exists('is_ssl'))
{
	function is_ssl() {
		return ( isset($_SERVER['HTTPS']) && 'on' == strtolower($_SERVER['HTTPS']) ) ? true : false;
	}
}

if(!function_exists('force_ssl_admin'))
{
	function force_ssl_admin($force = '') {
		static $forced;

		if ( '' != $force ) {
			$old_forced = $forced;
			$forced = $force;
			return $old_forced;
		}

		return $forced;
	}
}

if(!function_exists('force_ssl_login'))
{
	function force_ssl_login($force = '') {
		static $forced;

		if ( '' != $force ) {
			$old_forced = $forced;
			$forced = $force;
			return $old_forced;
		}

		return $forced;
	}
}

if(!function_exists('site_url'))
{
	function site_url($path = '', $scheme = null) {
	// should the list of allowed schemes be maintained elsewhere?
	$orig_scheme = $scheme;
	if ( !in_array($scheme, array('http', 'https')) ) {
		if ( ('login_post' == $scheme) && ( force_ssl_login() || force_ssl_admin() ) )
			$scheme = 'https';
		elseif ( ('login' == $scheme) && ( force_ssl_admin() ) )
			$scheme = 'https';
		elseif ( ('admin' == $scheme) && force_ssl_admin() )
			$scheme = 'https';
		else
			$scheme = ( is_ssl() ? 'https' : 'http' );
	}

	$url = str_replace( 'http://', "{$scheme}://", get_option('siteurl') );

	if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
		$url .= '/' . ltrim($path, '/');

	return apply_filters('site_url', $url, $path, $orig_scheme);
	}
}

if(!function_exists('admin_url'))
{
	function admin_url($path = '') {
	$url = site_url('wp-admin/', 'admin');

	if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
		$url .= ltrim($path, '/');

	return $url;
	}
}

if ( !defined('WP_CONTENT_URL') )
	define( 'WP_CONTENT_URL', get_option('siteurl') . '/wp-content'); // full url - WP_CONTENT_DIR is defined further up

if ( !defined('WP_PLUGIN_URL') )
	define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins' );

if(!function_exists('plugins_url'))
{
	function plugins_url($path = '')
	{
	$scheme = ( is_ssl() ? 'https' : 'http' );
	$url = WP_PLUGIN_URL;
	if ( 0 === strpos($url, 'http') ) {
		if ( is_ssl() )
			$url = str_replace( 'http://', "{$scheme}://", $url );
	}

	if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
		$url .= '/' . ltrim($path, '/');

	return $url;
	}
}

PHP IMAP Notes

Previously I wrote about using PHP’s IMAP functions. Now I’d like to provide a more detailed, and less wordy, overview of some of the more common IMAP functions. Enjoy!

imap_open

imap_open opens a connection to the specified mailbox. It takes up to 5 parameters:

Mailbox:
This is the address and port of the mail server you are connecting to. Typically this parameter will look like one of the following:

  • mail.domain.net:143
  • localhost
  • 127.0.0.1:110

The port number is optional, but it is much more efficient if one is provided. The default IMAP port is 143 and POP3 is 110

Username
Typically the email address you are trying to access mail from, but not necessarily.

Password
The password corresponding to the username

Options
The options, or flags, modify your connection stream. The default is /imap but I’ve found that /imap/novalidate-cert is often necessary. If you are trying to connect using POP3 you’ll have to set this to /pop3.

Retries
The number of connection retries. If your mail server limits the number of connection attempts it is important that this value be set to 0, otherwise you may lock yourself out of your account.

Don’t forget to close the connection stream using imap_close($mailbox) where mailbox is the connection stream you opened with imap_open.

imap_fetch_overview

imap_fetch_overview returns an overview of one or more emails. This function Always returns an array, even if only a single message is specified.

imap_stream
A connection stream created with imap_open

message id or sequence
imap_fetch_overview takes either a single message id, or a sequence formatted as x:y (ex. 1:5 will return message 1 through 5 inclusive).

Options
This parameter must be set to FT_UID if the sequence parameter is a sequence of UIDs rather than message ids. Using UIDs is often preferable to message ids because message ids are not guaranteed to remain the same. Some functions however, require that you use message ids.

This function returns a lot of useful information about a particular email. Read the function documentation for a full list. While this function does return to and from headers, it does not return the cc and bcc headers. You will have to use imap_headerinfo to obtain these. Note that imap_headerinfo cannot take a UID as a message parameter, you’ll have to use message ids.

imap_check

This function returns information about the specified mailbox. Most likely if you are using this function, you are using it to obtain the number of messages in the mailbox.

Setting and Unsetting Message Flags

Each message in a mailbox has a set of flags, probably the most interesting one is the SEEN flag. The seen flag indicates if the email has been opened or read. Usually this flag is set when using the imap_body function to obtain the body of a message. If you want to mark an email as seen you can use imap_setflag_full. To set the email as unread, use imap_clearflag_full to unset the SEEN flag.

Deleting Emails

The imap_delete flags an email for deletion. This function is really a shortcut for calling imap_setflag_full with the deleted flag. Likewise, imap_undelete is a shortcut for calling imap_clearflag_full with the deleted flag. Note that these functions do not actually delete the message. In order to delete the message you must either call imap_expunge or imap_close with the optional flag CL_EXPUNGE.

Searching the mailbox

The imap_search function is great for emails that meet a certain criteria. Personally I use this function mostly to find unopened emails.

PHP Error Handling

Previously I wrote about Exceptions as a way of dealing with error cases in PHP. Exceptions are a great way to anticipate errors and handle them appropriately. Unfortunately, however, it is not possible to anticipate every error and you users still might be presented with an ugly error message generated by PHP. One way to prevent users from seeing warnings or errors generated by PHP is to turn off error reporting. The down side to using this method is that you might not be aware that an error is occuring, and you’ll be even less aware of the details of that error.

A better solution would be to use a custom error handler. PHP provides you with a function that allows you to override the default error handler called set_error_handler. This function takes as input a callback, which is your custom error handling function. Once you call set_error_handler, PHP will no longer report errors as it normally would and instead pass those errors to your function. The function you define must take at least two inputs: error number, and error message, and optionally three additional parameters: error file, error line, and error context. I’ll go into more detail about this later, lets define our function:

set_error_handler('customHandler');

function customHandler($eNum, $eMsg, $eFile, $eLine, $eContext)
{
     switch(eNum)
     {
          case E_WARNING:
               echo "the following warning occured on line $eLine in $eFile: $eMsg";
               break;
          case E_NOTICE:
               echo "the following notice occured on line $eLine in $eFile: $eMsg";
               break;
          case E_ERROR:
               echo "the following error occured on line $eLine in $eFile: $eMsg";
               break;
     }
     return true;
}

In the above example we override the defualt error handler with our custom handler, customHandler. Anytime an error or warning occurs PHP will pass that error to our function. In this case, we use a switch statement to deal with each of our three error types: E_WARNING, E_NOTICE, and E_ERROR. Note that there are actually several more error types; you can find them Here.

Once the error has been passed to our function, we simply output the error message and the file and line number it occured in. This isn’t a whole lot different than the default behavior, but this code could easily be modified to create a database entry everytime an error occurs. Using this method, you could review all of the errors your users are getting as well as give them a customized error message. In the code above, I do not use the eContext parameter in my error reporting. eContext is an array containing of every variable at the time the error occured. Typically this is not something you show to a user, but can be very useful in debugging your program.

Note that the function we created returns true when it is complete. This is important. If it were to return false PHP would return control to the default error handler. If you wish to restore the default error handler you can use the restore_error_handler function.

Now that you know how to write a function that will override the default behavior when an error occurs, you might want to trigger your own errors that can be handled by this function. You can do this using the trigger_error function with an error message as the first parameter and optionally an error type as the second parameter. The defuault error type for trigger_error is E_USER_NOTICE, but it can also be set to E_USER_WARNING, or E_USER_ERROR. The method of triggering a user error and handling it with a custom error handler is similar to exception handling, though I would advice using this method only when reporting errors. If an error can be recovered from or there is an alternatice behavior that your program should use, exceptions are the way to go. If you want to present your user with custom error messages and track when errors occur, custom error handling is the way to go.

PHP Exception Handling

If you’ve ever coded in Java or C++ you probably know what exceptions are. If not, that’s okay. I’ll go over the basics.

Exceptions modify the control flow of a program. Usually, they are generated by functions when an error occurs. Throwing and catching exceptions allow the programmer to anticipate an error and handle it accordingly. For example: Say you write a function to open up a file and read its contents. When the function tries to open the file, however, it finds that the file does not exist. Generally this would just generate a nasty error and your program would terminate. If the programmer is aware that this is a possibility, however, he or she could check for that and throw an exception which would be caught and handled in a more user friendly fashion.

Enough talk lets see some code.

function open_file($fileName)
{
     if($contents = @file_get_contents($fileName))
          return $contents;
     else
          throw new Exception('Bad File Name!');
}

try
{
     $text = read_file('MyFile.txt');
}
catch(Exception $e)
{
     echo $e->getMessage();
}

In the code above, our function call, read_file, is surrounded by a try block which is followed by a catch block. The try block tells PHP that it should try to execute the code inside the block, while looking for the exception specified by the catch statement. The body of the catch statement tells PHP what to do in the event that exception is caught. In the case above if the file cannot be opened we simply echo ‘Bad File Name!’.

This example of course is trivial, but using exceptions you can better handle errors as they occur, which is bound to happen anytime user input is involved.

In the example above, a general exception is thrown. The exception itself is an object, the one above, has a constructor that takes as input a string. PHP, however, lets us define our own exceptions. Here’s how:

class CustomException extends Exception
{
     private $message;

     function __construct($message)
     {
          $this->message = $message;
     }

     function getMessage()
     {
          return $this->message;
     }
}

In the example above we define an exception called CustomException. Note that in the class declaration it states the CustomException extends Exception. In order for an exception to be thrown our custom exception class must extend Exception. A side effect of this is that if a function throws an exception within a try block, if the catch block is set to catch a general exception, it will catch ANY exception, including our CustomException. In the example, I create an exception class that is effectively identical to a general exception (though stripped down). You can however, define your exceptions in whatever way suits your needs.

It is important to remember when using exceptions, that if the exception occurs, code within the try block will stop executing. Be careful when putting code within a try block, only add code that cannot be executed if the exception occurs. For instance, if we call read_file, only code that needs the contents of that file should be inside the try block.

I Just Took a PHP Test…

So I was going through the process of signing up on a freelancers website, and the site offered various tests meant to prove proficiency in a given subject. I decided I’d give it a try so I took the PHP5 test. 30 minutes later I found out that I passed and did better than 66% of people that have taken the test. The only problem is that of the 40 questions, I can only think of one that was relevant to PHP programming. The remaining questions were completely obscure and pointless.

You might think that I’m just upset about not knowing the answers, but I actually did know the answers to most of them. That, however, doesn’t make them any less pointless. Let me give you an example. One of the questions asked what the output of the following code:

$arr = 'a';
$arr[0] = 'b';
echo $arr;
echo $arr[0];

The options were ‘ab’, ‘bb’, ‘b’, and an error. This is actually one of the better questions, but the reason why it is ridiculous is because you would never encounter code like this. What the question is attempting to determine, is your understanding of strings in PHP (strings are character arrays). The answer happens to be ‘bb’, but if you couldn’t figure that out don’t worry.

Here’s another example:

$x = 99;
$y = 9;
$z = 8;
echo $x++/$y++ + --$z;

If you can’t figure this out, that problem is not with you, but with the jackass that wrote this code. I honestly don’t know what this question is trying to show, other than your ability to stare at terrible code for 5 minutes trying to figure out what it does. I recall from my freshman year of college, taking intro to programming. Within the first week of this course, which assumed you had no knowledge of programming, everyone knew that this was bad code. In fact I recall a professor explicitly writing code similar to this on the board and telling us if we write our code like this, he won’t grade it. By the way I think the answer is 17.

Want another one? Okay, which of the following is a function that can help you determine if a function exists:

is_function ()
function_exists ()
fexists ()
None of the above

Even if I had never seen a line of PHP code in my life I could answer this by tabbing over to google. Every question on the test (with the exception of the one that I mentioned) fell into the category of completely pointless and obscure, or “Do you know how to look up what a function does using google”.

the one question that I mentioned that was actually a good question, was about outputting each value of an arbitrary list, that is a list that contain sub-lists as well as individual elements. The correct way to do this is with a recursive function and is_array().

So what’s the point of this rant you say? My point is that if you are a PHP programmer don’t waste time with these tests and definitely don’t spend money to take one. If you are looking for a PHP programmer don’t place any merit on the results of one of these tests. The only way to tell if someone is a good programmer is to see and run their code. Furthermore, the only way to become a good programmer is to write lots of code of your own, look at other people’s code, and get advice from experienced coders.

Validating User Input

Whenever you write an application that takes user input, you must assume users fall into two categories. Users who are incompetent, meaning that they are likely to provide incorrect input, and users that are attempting to exploit the system, meaning that they are trying to access, destroy, or manipulate information that they should not be able to. Obviously there is a third category: Users who neither malicious, nor incompetent and are using the system in good faith. In the context of making a secure and robust application, however, we do not care about this third group of users.

Robust Applications

An application is robust if it is not prone to crashing or misbehaving regardless of the input it is given. If an application is given improper input it should respond by informing the user of their mistake. This means that the programmer must determine the nature of a user’s input, before using it. If your program is expecting a number as input, it should not proceed if that input is a string. Furthermore, if only a certain range of numbers (ie 1 through 10) are valid, then the program should not proceed if given a number outside of that range.

Regular expressions can further aid in validating data. If you are expecting for a user to submit an email address, simply verifying that the input is a string is not sufficient. You want to ensure that the input meets certain criteria: It should consist of at least 1 character followed by the @ symbol followed by a domain name, the . symbol and finally, a TLD. A regular expression to accomplish this would be:

/^([a-zA-Z0-9])+([a-zA-Z0-9\.\\+=_-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/

Certainly there are more comprehensive ways to validate an email address, but it is important not to get carried away when validating data. The main purpose is not only to ensure that incorrect input is dealt with, but also that correct input is passed on without incident.

Secure Applications

In most cases incorrect input is harmless. It might cause the application to behave poorly or even crash, but generally restarting the programming or submitting a form over again will fix the problem. Some input, however, is intended to exploit a vulnerability in the system. An SQL injection is a common example of this type of input. Ensuring that input conforms to any applicable constraints is a first wave of defence against this type of attack. In addition to this, however, it is important to either escape or exclude certain characters from user input. Quotes for example should be be properly escaped.

Examples

So far I’ve talked about robust and secure applications in general. Now I will give examples of how to secure your application in PHP. Before I give code examples I would like to outline a few practices that will prevent SQL injections, as well as good faith mistakes:

  • Email addresses should be validated with a regular expression.
  • Number values, such as dates should be validated as integers.
  • User names should be constrained to a subset of characters (ie A-Z, a-z, 0-9 and _) and validated with a regular expression.
  • Passwords should be encrypted (i.e. sha1) before submitting to the database
  • All data should have quotes escaped

Here are a few PHP functions to validate user input:

<?php

/**
Ensures that the input is number, and if specified, lies
between the values min and max. For example, if you want to
validate that an input is a valid day of the month call
validateNumeric($input , $min = 0 , $max = 31);
**/
function validateNumeric($value , $min = 'none' , $max = 'none')
{
	if(!is_numeric($value))
		return false;

	if(is_numeric($min) && $min > $value)
		return false;

	if(is_numeric($max) && $max < $value)
		return false;

	return true;
}

/**
Ensures that the given email address is correctly
formatted
**/
function validateEmailAddress($address)
{
	if(!preg_match( "/^([a-zA-Z0-9])+([a-zA-Z0-9\.\\+=_-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $address))
	{
		return false;
	}
	return true;
}

/**
Ensures that the given username contains only
letters and numbers and is longer than the
give minimum length.
**/
function validateUsername($user , $minLength)
{
	if(eregi('[^A-Za-z0-9]', $u) > 0 || strlen($u) < $minLength)
	{
		return false;
	}

	return true;
}

/**
Escapes the given string. It is best to use whatever
real_escape_string method that PHP supplies for your
particular database. I use MySQL here as a default.
If no real_escape_string method exists, the addslashes
function is used.
**/
function escapeString($value)
{
	if(function_exists('mysql_real_escape_string'))
		return @mysql_real_escape_string($value);
	else
		return addslashes($value);
}

/**
Encrypts the given password using sha1 (twice).
Also supports the use of a salt, which is recommended.
**/
function encryptPassword($password , $salt = '')
{
	$hash = sha1( $salt . sha1($password) );
	return $hash;
}

?>

Accessing Email through IMAP using PHP

For a concise overview of some of the more common functions and a few tips read PHP IMAP Notes.

Previously I wrote about Intercepting Email with PHP and so far, that has been the most popular post on this blog. That being the case, I decided to write about how you can use PHP’s IMAP functions to access email.

The main difference between the two method is that when accessing email through IMAP you are accessing a mailbox that already exists. If you use piping to intercept an email you bypass the mailbox altogether. One advantage of using PHP’s IMAP functions to access Email is that you can avoid working with raw data, which would be the case with piping. With the functions provided by PHP, there would be nothing to stop you from building a webmail application with just as many capabilities as any of the major applications out there.

Before you can retrieve your email you must establish a connection to the mailbox. In order to do this you will need the IP and port of the mail server, along with a valid username and password. To connect to a mailbox on your local host use this code:

$host = 'localhost:143';
$user = 'user@domain.com';
$password = 'drowssap';
$mailbox = "{$host}INBOX";

$mbx = imap_open($mailbox , $user , $password);

Note that port 143 is the default, but is not always the correct port. If you don’t know the port you can use just the IP (or localhost) by itself, though it is recommended that you find the correct port. Also note that you can use this code to open a POP stream, as well as many other protocols. I’ll show you how to open a POP stream here, but for other protocols (IMAP over SSL, NNTP, etc.) you’ll have to read the documentation for imap_open.

$pophost = 'localhost:110/pop3';
$user = 'user@domain.com';
$password = 'drowssap';
$mailbox = "{$host}INBOX";

$mbx = imap_open($mailbox , $user , $password);

In this case as well, port 110 is the default but might not be the case for you.

Now that we’ve opened our IMAP (or other) stream we can start using PHP’s various IMAP functions to work with our mailbox. Note that if you’re stream is not an IMAP stream, not all of these functions will work as intended. Here I will show you to retrieve all of the emails in your inbox and how to view one of those emails, but I will only be using a small portion of the functions available to you, so you may want to take a look at the functions.

<?php
$mbx = imap_open($mailbox , $user , $password);

/*imap_check returns information about the mailbox
including mailbox name and number of messages*/
$check = imap_check($mbx);

/*imap_fetch_overview returns an overview for a message.
An overview contains information such as message subject,
sender, date, and if it has been seen. Note that it does
not contain the body of the message. Setting the second
parameter to "1:n" will cause it to return a sequence of messages*/
$overviews = imap_fetch_overview($mbox,"1:{$check->Nmsgs}");
?>
<table>
     <tr>
          <td>From</td>
          <td>Date</td>
          <td>Subject</td>
     </tr>

<?php
foreach($overviews as $overview)
{
     ?>
     <tr>
          <td><?php echo $overview->from; ?></td>
          <td><?php echo $overview->date; ?></td>
          <td><a href="open.php?id=<?php echo $overview->uid; ?>"><?php echo $overview->subject; ?></a></td>
     </tr>
     <?php
}
?>
</table>

The code above will generate a list of emails in your inbox, similar to what you would see if you logged into your email account. Each subject line is a link to open.php which is an arbitrary script that will open the message with the corresponding uid. The code below is an example of what open.php might look like:

<?php
$uid = $_GET['id']; //I won't validate this input, but you should

$mbx = imap_open($mailbox , $user , $password);
$overview = imap_fetch_overview($mbx, $uid, FT_UID);
$body = imap_body($mbx, $uid, FT_UID);
?>
<table>
     <tr>
          <td>From:</td>
          <td><?php echo $overview->from; ?></td>
     </tr>
     <tr>
          <td>Date:</td>
          <td><?php echo $overview->date; ?></td>
     </tr>
     <tr>
          <td>Subject:</td>
          <td><?php echo $overview->subject; ?></td>
     </tr>
     <tr>
          <td colspan="2"><?php echo $body; ?></td>
     </tr>
</table>

Notice that I set the FT_UID parameter in order to use a UID rather than a message_id. A UID will always remain the same, while a message_id may change as the content of the mailbox changes. The script would most likely work with message ids, but UIDs are guaranteed to work. If you are using a POP stream, you might find that in some cases you have to use message_ids in place of UIDs due to compatibility issues.

The imap_body function returns the body of the message with the corresponding id, but it also sets the message to “seen”. If you do not want to mark the message as seen, pass it the FT_PEEK option.

There is a great deal that you can do with these functions; far too much for me to detail all of it here. If there is anything in particular you would like to see, feel free to post a comment detailing your question.

PDO… Use It

The majority of PHP code I see, whether it be posted by a beginner on a forum, or built into a large application, seems to suggest that the current standard when accessing a database is to make use of whatever database specific commands PHP provides for your particular database (ie mysql_query, mssql_query, etc). Often times, calls to these function are coupled with various attempts to prevent sql injections (such as calls to mysql_real_escape_string). Altogether, however, to me it seems clumsy and insecure.

PHP5 provies a built in (partial) database abstraction layer that can not only simplify the process of querying your database not only protecting against sql injections, but optimize your queries and make your application more portable: PDO (PHP Data Objects). A typical database transaction with PHP might look something like this:

mysql_connect('localhost', 'user', 'password');
mysql_select_db('myDB');

$data = mysql_real_escape_string($_POST['data']);
$query = 'SELECT column FROM table WHERE data = \'' . $data . '\'';

$result = mysql_query($query);
while($row = mysql_fetch_array($result, FETCH_NUM))
{
     echo $row[0];
}

Now with PDO

$dsn = 'mysql:dbname=myDB;host=127.0.0.1';
try {
     $db = new PDO($dsn , 'user' , 'password');
}
catch(PDOException $e) {
     echo $e->getMessage();
}

$query = 'SELECT column FROM table WHERE data = ?';
$statement = $db->prepare($query);
$statement->bindParam(1 , $_POST['data']);
$statement->execute();

$rows = $statement->fetchAll(PDO::FETCH_NUM);

foreach($rows as $row)
{
     echo $row[0];
}

Outwardly, the second example might not seem like an improvement on the first. What is important, however, is what is going on underneath. From PHP.net:

Calling PDO::prepare() and PDOStatement::execute() for statements that will be issued multiple times with different parameter values optimizes the performance of your application by allowing the driver to negotiate client and/or server side caching of the query plan and meta information, and helps to prevent SQL injection attacks by eliminating the need to manually quote the parameters.

In addition to this, it simplifies the process of making your application compatible with another database. If you were to use database specific function throughout your application, you would need to change each to allow it to work with another database.

PDO, however, is not a complete database abstraction layer. While it does aid you in making your application portable, it will not emulate features of a particular database. If your queries are incompatible with your database PDO will not help you. Rarely, however, will it be the case that your queries are not compatible with most sql databases.

There are actually a few cases where where PDO will not be able to execute your queries (example: http://dev.mysql.com/doc/refman/5.0/en/c-api-prepared-statement-problems.html) but these cases are few and far between. Really it is rarely the case that you should NOT be using PDO.

Using PDO does not guarantee that user data is safe. You should always validate and sanitize user input, but PDO provides a decent extra line of defense.

http://us.php.net/pdo