My Blog

contains PHP and other web related content. (Sometimes there are some off topic things - don't freak out!)

Archive for the ‘PHP’ Category

How I test email recipients when I develop

Tuesday, August 31st, 2010

When developing an application, there are usually various different environments that you run the code in. First is the development environment. Next, you have the QA or test environment, staging, and then live or production. It stands to reason that if you are using outgoing e-mail in your application, and your application is in production, it should send to the proper recipients. However, what do you do in testing and development?

The Old Way: One Email to Rule Them All

The old way of testing was simple: If live, send to proper email address. If not live, send to my developer test address. So, if I ran a process, I might see something like 8 emails in my developer test box. The problem with this, however, is the only inkling I have that each e-mail is different is by any custom information inside of the email. Hopefully it says “Dear John Smith, ” or “Dear Suzie Q”. You also have no way of verifying if the e-mail address you retrieved for this particular message was in fact the right one – mainly because you overwrote it with a test one.

The New Way: Combining Test and Production Emails

Real quick, take a look at this RFC… Just kidding. But, after understanding the various formats of an e-mail address, I got a great idea. I should be able to use the + sign with the original email address and my developer test address. I may just need to replace the @ sign with _AT_. Why? Whenever you create an e-mail address, you can add the + sign after your user part of the email address to add additional tag information to it. So, if my e-mail address was thedude@guy.com, I will also receive e-mail if it is addressed to thedude+spammysite@guy.com.

So, for my code, if not in production, I’m going to replace the outgoing e-mail address with a specially formatted version of the email that contains the destination address but gets directed to my test email. So, in this case, my testing email address is test@aaronsaray.com. The outgoing address is newuserguy@hotmail.com. The resulting outgoing email address will be test+newuserguy_AT_hotmail.com@aaronsaray.com. It will enter my test e-mail box but I can still verify that the recipient would have been right.

Here is a bit of code I use to accomplish this:

1
2
3
4
5
6
$to = 'outgoing@email.com';
if (ENVIRONMENT != 'LIVE') {
    $parts = explode('@', 'test@aaronsaray.com');
    $to = str_replace('@', '_AT_', $to);
    $to = $parts[0] . '+' . $to . '@' . $parts[1];
}

Easy MCrypt encryption class

Tuesday, August 17th, 2010

For whatever reason, I can never remember the exact coding of MCrypt. And maybe that is a good thing – so I stop doing so much code duplication and start using a class I wrote. For this reason, I’ll save you the same frustrations and share how I do my encryption.

easyMcrypt.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class easyMcrypt
{
	protected static $_openModules = array();
 
	public static function encrypt($string, $key, $type, $mode)
	{
		$module = self::_getModule($type, $mode);
		$iv = self::_alt_mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
		mcrypt_generic_init($module, $key, $iv);
		$data = mcrypt_generic($module, $string);
		mcrypt_generic_deinit($module);
		return $data;
	}
 
	public static function decrypt($string, $key, $type, $mode)
	{
		$module = self::_getModule($type, $mode);
		$iv = self::_alt_mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
		mcrypt_generic_init($module, $key, $iv);
		$data = trim(mdecrypt_generic($module, $string));
		mcrypt_generic_deinit($module);
		return $data;
	}
 
	protected static function _getModule($type, $mode)
	{
		if (!isset(self::$_openModules[$type][$mode])) {
			if (in_array($type, mcrypt_list_algorithms()) && in_array($mode, mcrypt_list_modes())) {
				self::$_openModules[$type][$mode] = mcrypt_module_open($type, '', $mode, '');	
			}
			else {
				throw new exception("{$type} is not a valid algorithm");
			}
		}
 
		return self::$_openModules[$type][$mode];
	}
 
	/** borrowed from http://www.php.net/manual/en/function.mcrypt-create-iv.php#54925 **/
	protected static function _alt_mcrypt_create_iv($size)
	{
		$iv = '';
	    for($i = 0; $i < $size; $i++) {
	        $iv .= chr(rand(0,255));
	    }
	    return $iv;
	}
}

This will be envoked by using the static functions encrypt() and decrypt(). It is very rare that I will use both encrypt and decrypt on the same program flow, but I may end up using encrypt() twice or more. This is the reason why I do some caching in the class. Notice, both the encrypt and decrypt methods will get the module from the protected _getModule() method. This sends in the type and the mode. This method checks to see if we’ve already set this module open. If so, we don’t need to open it again – just return it from the protected cache. Otherwise, a check is done to make sure that the type and mode exist. If so, it is stored in the cache. Finally the stored module is returned. If it does not exist, an exception is thrown. I did this check – even though it seems a little redundant because why would you check to see if you’re using say…tripledes – you should just know – I did it because I had some instances where a non found mode/type just returned a NULL result. The code executed fine. Bah!

At any rate then, a new Initialization Vector is generated. In this case, I used one of the functions I found on the PHP manual page. I was running into the delay on some OS’s / versions of PHP with the mcrypt version of IV_CREATE. Then, the module is init’d, the data is either encrypted or decrypted, and the module is de-init’d. The data is then returned (note: the decryption one trims spaces at the end that can sometimes happen).

For an example of how this is used:

1
2
3
4
5
6
7
8
9
10
$string = 'Please encrypt me';
$key = 'this is my encryption key';
$type = 'tripledes';
$mode = 'ecb';
 
$encrypted = easyMcrypt::encrypt($string, $key, $type, $mode);
var_dump($encrypted);
 
$decrypted = easyMcrypt::decrypt($encrypted, $key, $type, $mode);
var_dump($decrypted);

Hide Email Addresses while still using mailto: Header Redirect

Tuesday, August 3rd, 2010

Everyone I’ve spoken to recently no longer puts mailto:// links in their code for fear that the owner of that address will get more spam. However, there are still legitimate uses for a link like this. In order to foil very simple email parsing bots, I’ve come up with the following script.

Let’s say you have the website http://blahblah.com – and on that website, user joe would like to have his email address of joe@blahblah.com accessible via a mailto:// link. He doesn’t want people to use a contact form – but doesn’t want spam either. I would form his e-mail link in the following manner:

1
And, if you would like to contact joe, you can <a href="email.php?user=joe">email joe</a> directly.

The content of the PHP file would be the following:

1
2
3
4
5
6
7
8
9
$user = isset($_GET['user']) ? $_GET['user'] : '';
 
if ($user) {
	$email = $user . '@' . $_SERVER['SERVER_NAME'];
	header("Location: mailto://{$email}");
}
else {
	header('HTTP/1.0 404 Not Found');
}

This simply forms the proper e-mail address from the user get parameter and sends back a different redirect. The thought is simple email scrapers will not detect it as an email address and will leave the ‘link’ alone.

Final thought: if your site requires javascript, you could put an onclick handler on the link to send another parameter along with the script. The script could then verify that the onclick has happened by checking the presence of the additional GET parameter. If it is not present, it could be attributed to a bot (that doesn’t handle javascript, hopefully) – and not provide the e-mail address.

Facebook Message System – in PHP

Tuesday, July 27th, 2010

So Facebook has been really cool in the way that they have designed and implemented some new paradigms in the electronic communication realm. However, one thing is a problem: they’re too smart. They have hired the best of the best – and have made that the norm. The rest of us are struggling to keep up.

I was recently faced with a task: Make our message system like facebook’s. OK – seems easy. I implemented what I thought would be the solution -and it worked. Sorta. But now, I’m hearing there are bugs. Uh oh. … I think I’ve fixed them all – but I started from scratch. This is what I should have done.

Define the requirements

It’s easy to say that the system should be like Facebook’s. However, how does FB actually handle the messages? What does the user really experience in the UI/UX? Let’s define some requirements:

  • 1 person can send to 1 or more people
  • No subject – just message (this isn’t entirely like Facebook – but its a requirement I put on it)
  • Response comes to everyone
  • Delete message deletes all replies/threads from now back to inception. If a new response comes, it starts a new ‘thread’ to that user
  • Sent mail only shows messages you’ve sent that aren’t already in your inbox
  • Needs to show who the entire thread message is between
  • Show new messages that are unread
  • backend: no dupe messages – meaning – the body of the message will not be duplicated to each user- normalized tables
  • You should not see a message in your inbox if you’re the sender and there are no responses yet

So there are the specs, lets do this

In order to demonstrate some of these features and practices, I’m going to have to jump ahead to the final product. I will do my best to explain why I came up with those things, however

Create the MySQL tables

In order to keep the normalized feel of all of this, we’ll need to create two tables. The first table will be for the message itself. It will contain the originator or author, when it was created, where it was created (IP), and the body of the message. Side note: The tables will all be prefixed with a ’2′ because this is my second time trying to do this… hopefully this time is successful! :)

1
2
3
4
5
6
7
8
9
CREATE TABLE `message2` (
  `mid` int(10) unsigned NOT NULL auto_increment,
  `seq` int(10) unsigned NOT NULL default '1',
  `created_on` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `created_on_ip` varchar(16) NOT NULL,
  `created_by` int(10) unsigned NOT NULL,
  `body` text NOT NULL,
  PRIMARY KEY  USING BTREE (`mid`,`seq`)
) ENGINE=InnoDB;

Things to note about this table:
First, the MID column is the message ID. Think of each initial message as the beginning of a thread. Instead of having unique message IDs that are all parent/children, I decided to have one main thread (MID) and then have messages in it – which are all defined by the SEQ (sequence number) column. The rest is pretty self explanatory. The created_by and all subsequent user identifications will be numeric ID’s that may point to a different user ID somewhere else. The last thing to note is that the primary key is based on the MID and the SEQ – you will never have more than one entry per MID/SEQ.

Next, it’s time to make the recipient table. All of the recipients that receive this need to have a pointer to that MID/SEQ message. Also, the sender technically is a recipient as well. (Later on, we’ll develop SQL to not show initial sent responses in the ‘inbox’ for the user who sent them – as that makes no sense).

1
2
3
4
5
6
7
8
CREATE TABLE `message2_recips` (
  `mid` int(10) unsigned NOT NULL,
  `seq` int(10) unsigned NOT NULL,
  `uid` int(10) unsigned NOT NULL,
  `status` char(1) NOT NULL default 'N',
  KEY `m2r1` USING BTREE (`mid`,`status`),
  KEY `m2r2` USING BTREE (`uid`,`status`)
) ENGINE=InnoDB;

This table has the MID/SEQ identifier to point to the message that the users are receiving. It then has the UID column – which is in a similar format to the message2.created_by column. Finally, we have a STATUS column – which will be N for new, A for active (or read) and D for deleted. Two indexes are defined on this table. These will not make sense until we look at the SQL that is used to generate the searches for messages. We’ll do that next.

Creating the PHP

For demonstration purposes, we’re going to include a file called currentuser.php. All this does is set the $currentUser variable to an integer. In your full featured product, you’d probably use some sort of authentication/session system. Here it is:

currentuser.php

1
2
<?php
$currentUser = 1;

Now, the first view of the user will probably be their inbox. If they have no messages to view, it has to say so.
Note: I am using PDO and not a lot of security based programming in this example. Please look at the concepts and don’t just copy/paste the code.
Second note: I am not really using valid HTML or pretty pages either. The focus is on the PHP system in this case.

inbox.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
include ('currentuser.php');
print "<h1>Acting as {$currentUser}</h1>";
print "<h2>Inbox</h2>";
 
$dsn = 'mysql:host=db-1.local;dbname=c3';
$PDO = new PDO($dsn, 'user', 'pass');
 
$sql = "
select m.mid, m.seq, m.created_on, m.created_by, m.body, r.status from message2_recips r
inner join message2 m on m.mid=r.mid and m.seq=r.seq
where r.uid=? and r.status in ('A', 'N')
and r.seq=(select max(rr.seq) from message2_recips rr where rr.mid=m.mid and rr.status in ('A', 'N'))
and if (m.seq=1 and m.created_by=?, 1=0, 1=1)
order by created_on desc";
 
$stmt = $PDO->prepare($sql);
$args = array($currentUser, $currentUser);
 
if (!$stmt->execute($args)) {
	die('error');
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($rows)) {
	print '<table><tr><th>Originator</th><th>When</th><th>Body</th>';
        print '<th>Status</th><th>View</th></tr>';
 
	foreach ($rows as $row) {
		echo '<tr><td>' . $row['created_by'] . '</td><td>' . $row['created_on'];
                echo  '</td><td>' . $row['body'] . '</td><td>' . $row['status'] . '</td><td>';
		echo '<a href="view.php?id=' . $row['mid'] . '">View</a>'; 
		echo '</td></tr>';
	}
	echo '</table>';
}
else {
	echo 'No items in your inbox';
}
echo '<div><a href="compose.php">compose</a></div>';
echo '<div><a href="sent.php">sent</a></div>';

For our demonstration, the top of the pages will always say what ‘view’ this is and what user we’re acting as. Your full functional site may have different features to identify this. Also, at the top of each page, we’re including our currentuser.php file and making a connection to the database. This is all pretty standard stuff.

Next, you’ll see the MySQL query I came up with. First thing is to get both of the identifiers for the message (MID/SEQ), when it was created (so we can show the date), who created it (so we can show the originator or who it is ‘from’), and the status. The status will just be used to show if that message is new.

The sql gets the data from the recips table first. This is the pointer to all of the ‘copies’ of the initial message that should be available. Note that the message table itself is joined on so we can get the actual content of the message. Next, the recipient UID is verified to be the current user and the message must be either New or Active. Next, the sequence number must be a specific one. In this case a subselect is done. The maximum sequence number (so that would make it the newest) from the recips table where that message is the current message and the status is not deleted. In this case we don’t verify that the UID of that subselect is any user because we want to show any originator whether it be ourself or someone else. The last part of the where clause verifies that the sequence number is not 1 and that its not created by our current user. If it is 1, that means its the first message of the thread, created by us, and that we shouldn’t select it. Your inbox never shows items that you have originally sent but received no responses.

Then, the rest is pretty simple. All of the items are retrieved. A loop is generated and each ‘newest’ message is shown with a link to view it. Notice how the view link only has the MID, however. We don’t need to know the sequence number as we’ll be showing the entire thread.

Now, let’s actually view one of the messages.

view.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
include ('currentuser.php');
print "<h1>Acting as {$currentUser}</h1>";
print "<h2>Viewing a single message: " . $_GET['id'] . "</h2>";
 
$dsn = 'mysql:host=db-1.local;dbname=c3';
$PDO = new PDO($dsn, 'user', 'pass');
 
$sql = "
select m.mid, m.seq, m.created_on, m.created_by, m.body, r.status from message2_recips r
inner join message2 m on m.mid=r.mid and m.seq=r.seq
where r.uid=? and m.mid=? and r.status in ('A', 'N')";
$stmt = $PDO->prepare($sql);
$args = array($currentUser, $_GET['id']);
 
if (!$stmt->execute($args)) {
	die('error');
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($rows)) {
	/** get all of the people this is between **/
	$sql = "select distinct(uid) as uid from message2_recips where mid=?";
	$stmt = $PDO->prepare($sql);
	$args = array($_GET['id']);
	$stmt->execute($args);
	$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
	$uids = array();
	foreach ($results as $result) {
		$uids[] = $result['uid'];
	}
	$last = array_pop($uids);
 
	print '<p>Conversation between ';
	print implode(', ', $uids) . ' and ' . $last;	
	echo '.</p>';
 
	print '<table><tr><th>Originator</th><th>When</th><th>Body</th></tr>';
	foreach ($rows as $row) {
		echo '<tr><td>' . $row['created_by'] . '</td><td>' . $row['created_on'];
                echo '</td><td>' . $row['body'] . '</td></tr>';
	}
	echo '</table>';
 
	/** now update the message to viewed **/
	$sql = "update message2_recips set status='A' where status='N' and mid=? and uid=?";
	$stmt = $PDO->prepare($sql);
	$args = array($_GET['id'], $currentUser);
	$stmt->execute($args);
 
	echo '<form action="post.php" method="post">';
	echo '<strong>Reply:</strong><br />';
	echo '<textarea name="body"></textarea><br />';
	echo '<input type="hidden" name="mid" value="' . $row['mid'] . '" />';
	echo '<input type="submit" value="reply" /></form>';
}
else {
	echo 'Cannot find this message';
}
echo '<div><a href="inbox.php">Inbox</a></div>';
echo '<div><a href="delete.php?id=' . $_GET['id'] . '">Delete</a></div>';

As mentioned before, the header is the identification of the current user, the current page we’re viewing, and a connection to the database.

The sql statement gathers the same information as the inbox and joins like the inbox did. However, the where statement is a bit more simple. It restricts the recips it gets to the current user using the UID field, which is cool. (Remember, we will receive ‘replies’ from both messages we send as well as messages other people send). It also verifies that the message ID (MID) is what we’re hoping to view and that the replies that we are looking to view are not deleted.

It’s important to note the importance of that last part of the where statement. As per our requirements, it is possible to delete replies that we ‘own’. However, it can still happen that someone can reply after that and we will see more of the message, just not the parts we’ve decided to delete. This is the reason for that statement.

The statement is executed and if there are results, we continue on. As per the requirements, we also need to get all of the users who this conversation is through. We gather all of the distinct UID from the recips table where the message ID is the one we’re looking at. Then, using PHP, we gather them all into a numerically keyed array and POP off the last one. Then, we can implode the array with commas – and then add the last one using ‘and’. This way, if we have 4 recipients on this thread, it may look like this:

1,2,3 and 4.

If we only had two, it would look like this:

1 and 2

Next, a table is built and all of the replies are listed. An update statement is ran to update all of the ‘New’ replies as seen or ‘Active’ for the current user. Finally, an update form is shown. You can add a reply to the message using this form. Note, the hidden input of the MID is in this form. This posts to the file post.php – which is the same file that compose.php will post to. We’ll cover this later. The final link on this page is the delete.php file. It has the ID of the current message. Let’s take a look at this.

delete.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
include ('currentuser.php');
$dsn = 'mysql:host=db-1.local;dbname=c3';
$PDO = new PDO($dsn, 'user', 'pass', array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
 
$mid = isset($_GET['id']) ? $_GET['id'] : 0;
$sql = "update message2_recips set status='D' where mid=? and status != 'D' and uid=?";
$stmt = $PDO->prepare($sql);
$args = array($mid, $currentUser);
 
if (!$stmt->execute($args)) {
	die('error');
}
 
die(header('Location: inbox.php'));

This is pretty simple. Remember, we can’t delete the message itself. We can only delete the current user’s reply / message pointers as of up to now. So, the sql statement will mark all of them status of ‘D’ for deleted where the status previously was not deleted, the message ID is the current one and the user ID is our current user. Then, back to our inbox.

In order to view our sent items, the layout is pretty similar to the inbox.php file. However, the SQL statement changes. I will show just that here:
sent.php
….snip…

1
2
3
4
5
6
select m.mid, m.seq, m.created_on, m.created_by, m.body, r.status from message2_recips r
inner join message2 m on m.mid=r.mid and m.seq=r.seq
where m.created_by=? and r.uid=?
and r.status != 'D'
and m.seq=(select max(rr.seq) from message2_recips rr where rr.mid=m.mid and rr.status != 'D' and rr.uid=?)
order by created_on desc

…. snip ….

As with every other statement, the message ID, sequence, date, owner, status and boy are gathered. The message table is joined onto the recips table. Then, it has to be created by the current user. To match up the proper row, the r.uid matches the m.created_by as current user. The recip record must also not be deleted. Finally, the sequence number is the maximum from the replies where it is not deleted status and the message reply belongs to the current user. This will show both messages where multiple replies are received and the current user is the last sender – as well as messages where the user is the only sender (unlike the inbox).

Next, we have to make a new message form. This will submit to post.php – the same as the replies to a current message.
compose.php

1
2
3
4
5
<form action="post.php" method="post">
	To who (csv): <input type="text" name="uids" /><br />
	Message: <textarea name="body"></textarea><br />
	<input type="submit" value="send" />
</form>

Finally, we will add messages using the post.php file. It will handle replies and new messages. Depending on whether a message ID is being sent will determine if it is a new message or a reply.

post.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
include ('currentuser.php');
$dsn = 'mysql:host=db-1.local;dbname=c3';
$PDO = new PDO($dsn, 'user', 'pass');
 
$mid = isset($_POST['mid']) ? $_POST['mid'] : 0;
$body = $_POST['body'];
 
if (!empty($mid)) {
	/** get the recips first **/
	$sql = "SELECT distinct(uid) as uid FROM message2_recips m where mid=?";
	$stmt = $PDO->prepare($sql);
	$args = array($mid);
	if (!$stmt->execute($args)) {
		die('error');
	}
	$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
 
	/** get seq # **/
	$sql = "select max(seq)+1 as seq from message2 where mid=?";
	$args = array($mid);
	$stmt = $PDO->prepare($sql);
	$stmt->execute($args);
	$row = $stmt->fetch(PDO::FETCH_ASSOC);
	$seq = $row['seq'];
}
else {
	$seq = 1;
	$uids = explode(',', $_POST['uids']);
	$uids[] = $currentUser;
	$uids = array_unique($uids);
	$rows = array();
	foreach ($uids as $uid) {
		$rows[] = array('uid'=>$uid);
	}
}
 
if (count($rows)) {
	$sql = "insert into message2 (mid, seq, created_on_ip, created_by, body) values (?, ?, ?, ?, ?)";
	$args = array($mid, $seq, '1.2.2.1', $currentUser, $body);
	$stmt = $PDO->prepare($sql);
	$stmt->execute($args);
 
	if (empty($mid)) {
		$mid = $PDO->lastInsertId();
	}
 
	$insertSql = "insert into message2_recips values ";
	$holders = array();
	$params = array();
	foreach ($rows as $row) {
		$holders[] = "(?, ?, ?, ?)";
		$params[] = $mid;
		$params[] = $seq;
		$params[] = $row['uid'];
		$params[] = $row['uid'] == $currentUser ? 'A' : 'N';
	}
	$insertSql .= implode(',', $holders);
	$stmt = $PDO->prepare($insertSql);
	$stmt->execute($params);
 
	die(header('Location: view.php?id=' . $mid));
}
else {
	die('no recips found');
}

This is one of the most important pieces of this puzzle, so let’s analyze it carefully. First, the current user is obtained and the connection is made. Then, the message ID is retrieved from the POST request (if its a reply) or its set at 0 (for new messages from compose.php). $body is retrieved from the body key of the POST arrray.

If not empty MID, meaning if this is a reply, then get all of the recipients from the table where this ID exists. It is necessary to send the new message recip pointers to all of them. Then, get the next sequence number which will be the current maximum plus one, obviously.

Else, if empty MID, meaning it is a new message, then set the sequence number to 1. Make an array out of the user ID’s that were sent. The current user would only send to people NOT to itself, so we add the current user onto that. Then, the IDs are uniqued – and the $rows variable is prepopulated to be in the same format as it would be from the database.

Next, check to make sure the $rows variable is populated. This is just a sanity check – in all cases it should already be populated. If not, generate an error. Otherwise, insert the message into the message2 table using the proper message ID (0 for new message will generate a new autoincrement value), sequence number, user IP, created_by (which is our current user) and the body of the message.

If we previously had an empty message ID, it is a new message – so we’ll get the last INSERT ID from the query. This is our new Message ID. This is needed for the insertions into the recipients table. To do so, it is going to be a dynamically built query. The first part is created and then holders/params variables are defined. Now, each item in the $rows array are looped through. Enough question marks for the prepared statement are added, and the rest of the parameters matching are filled in. The only thing of note is that if the current row’s user ID is the current user, then the status is ‘A’ for active. (they’ve obviously read it already). Otherwise, it is always ‘N’ for new.

The combined statement is now executed and the user is directed to look at the message they’ve created.

Ending Thoughts

While this is not yet a perfect/polished solution, I think it is further along the line. Old message systems used to duplicate a lot of messages and not allow for multiple recipients. I think this walks the line of being similar to the older systems but working with new paradigms. Are there places where I could make this better? Have any suggestions? Please let me know. :)

Book Review: Expert PHP 5 Tools

Tuesday, June 29th, 2010

Where was this book 4 years ago? Or even 2 years ago? (Uh – it was published in 4/2010… so… just in the author’s head apparently). Anyway, I digress. I want to tell you about the book Expert PHP 5 Tools from Packt Publishing. At a beginner or intermediate position in my career, this would have been my bible. It is clearly written and introduces topics cleanly and easily. (As an author myself, I want to say KUDOS to this guy – Dirk Merkel – you did a great job.) From design, coding, testing and deployment, this book touches on it all. You’ll find out about coding standards (what they are, and why they’re needed), Documentation with phpDocumentor, Eclipse PDT – and Zend Studio – and a comparison of both, Source control with SVN – as well as some tips (nothing like the SVN redbook – but that’s not what this is supposed to be!) – Debugging options, PHP frameworks overview, PHPUnit and Phing. It rounds out the topic with continuous integration systems like XINC and PHPUnderControl. Overall, I’ve never seen a book touch on so many topics that any PHP programmer should know.

Where it’s good:
Like I said, it introduces a lot of topics. It also gives why. It gives the introduction to them, some screen shots and examples.

Where it’s not so good:
It doesn’t cover GIT, and the information is sometimes too simple. It would benefit from giving search terms for googling more information on the topics its discussing.

Conclusion:
If you don’t know any one of the items I listed above in the description, chances are your knowledge of the other items is average as well. Grab this book and learn what you’re missing. Newer programmers – BUY THIS. If you don’t understand everything, that’s ok. Save it and read it again in 6 to 12 months.

Looking to check it out yourself?
You can buy a copy of it now. Not sold yet? Check out a sample chapter in PDF format.

When uniqid is too slow in PHP

Tuesday, June 8th, 2010

I just profiled some of my code and found out that the biggest chunk of my processing time was used by uniqid(). I use this to generate form tokens to prevent cross site request forgeries. On one page, I have 6 forms each with its own unique uniqid().

The first thought is to just use one ID per page. However, I didn’t want to change too much of my code. It’s working – so lets just make it work faster. I did some thinking and realized that maybe my unique id didn’t need to be THAT unique – just not super predictable. A sha1() hash of a random number should do the trick. And it should be faster. Just to verify, I did my own benchmark using this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$start = $stop = array();
 
$start['uniqid'] = microtime(TRUE);
for ($x = 0; $x< 1000; $x++) {
	$val = uniqid();
}
$stop['uniqid'] = microtime(TRUE);
 
$start['mt_rand'] = microtime(TRUE);
for ($x=0; $x<1000; $x++) {
	$val = mt_rand(0, 1000000);
}
$stop['mt_rand'] = microtime(TRUE);
 
$start['sha1/mt_rand'] = microtime(TRUE);
for ($x=0; $x<1000; $x++) {
	$val = sha1(mt_rand(0, 1000000));
}
$stop['sha1/mt_rand'] = microtime(TRUE);
 
foreach ($start as $key=>$startval) {
	echo "{$key}: " . ($stop[$key] - $startval) . "<br />";
}

The results:

uniqid: 1.1227629184723
mt_rand: 0.0030300617218018
sha1/mt_rand: 0.0076968669891357

As you can see, sha1/mt_rand combination is so much faster. In fact, 140x! While this is still micro-optimization, running that 6 times to me makes a difference.

Your thoughts? Is this still unique enough for form tokens?

How to Log PHP Errors like a Pro

Tuesday, May 25th, 2010

The error log can be fun to parse through and figure out what happened. Ok, so if you just read that and agreed, you need to move on. This is not for you. That’s not fun. However, you CAN make error gathering easier on yourself by including the right information in the error log. To top it off, you can present your users with something that is a bit more ‘friendly’ than the standard error display or blank page. Let’s check it out.

Create an Error Handling Class

All of my error handling is going to be pretty much uniform. In order to do this, I want to share a lot of code. I’m going to do this by creating a class and having my error handling methods a part of it. I plan on gathering data from both standard PHP errors and uncaught PHP Exceptions.

First, PHP Errors

The first thing I want to do is grab my PHP errors. I’ll make the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class errorhandlers
{
	public static function error_handler($errno, $errstr, $errfile, $errline, $errcontext)
	{
		$string = "ErrorNo: {$errno}:: {$errstr} || {$errfile} on {$errline} || ";
		ob_start();
		var_dump($errcontext);
		debug_print_backtrace();
		$string .= ob_get_clean();
 
		if (ENVIRONMENT == 'DEV') {
			print $string;			
		}
		else {
			error_log($string);
		}
 
		switch ($errno) {
			case E_NOTICE:
			case E_USER_NOTICE:
				//do nothing
				break;
 
			default:
				self::beFriendly();
				break;
		}
	}
 
	protected static function beFriendly()
	{
		/**
		 * kind of hacky
		 */
		if (ENVIRONMENT != 'DEV') {
			die(header("Location: /error"));
		}
	}
}

The first thing that is done is to grab all of the error context that PHP sends to the error handler. That is what the 5 parameters are for. I begin by making a string with this information in it. The last parameter is actually an array, so I use var_dump(). Before that, however, the output buffering is initiated. Then, the context is var_dump()’d. Finally, to get a little bit more context, the command debug_print_backtrace() is used. The contents of this output buffer is then added to the string.

The reason I am using ob_start() and not using the parameters / functions to return the data is because of a recursion issue that can happen with var_export()/var_dump() in certain contexts. I’m not sure what happened, but it was an infinite type of recursion – so I chose this method.

The final two steps of that method are pretty straight forward. If we are working in our development environment, print the error information to the screen. Otherwise, log it to the standard error log. (This is better than using ini_set() with display errors because of the extra context I’m adding with the debug_print_backtrace()). Finally, if not a NOTICE type error, call the beFriendly() static protected method.

The beFriendly() method simply redirects a user to a friendlier “ruh roh” type page if we’re not in the development environment.

Do something with uncaught exceptions

To handle exceptions, the following method is added to the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
	public static function exception_handler($exception)
	{
		$string = str_replace("\n", ' ', var_export($exception, TRUE));
 
		if (ENVIRONMENT == 'DEV') {
			print $string;			
		}
		else {
			error_log($string);
		}
 
		self::beFriendly();
	}

This is much more simple. The exception details are exported to a string. The new lines are removed because they play havoc with the error log grep’ing. Then, as with the previous method, it is either displayed or logged and then the user is redirected possibly.

Register the Error Handlers

The last thing to do is to register each of these error handlers. That is done with this simple code:

1
2
set_error_handler(array('errorhandlers', 'error_handler'));
set_exception_handler(array('errorhandlers', 'exception_handler'));

And then, you’re ready to go. Happy error logging!

Load Facebook Fanbox Faster by Caching it

Tuesday, April 27th, 2010

I wasn’t in favor of the Facebook fanbox on the site I was working on… but that’s what the client wanted – and that is what they get. I added it and moved on. Well, later, I started noticing a bit of errors in my Javascript Error log. I looked back at the newest edition: the fanbox. Depending on where I was connecting from, that box would take another 3 to 20 seconds to load. During that time, it was causing my page to appear to keep loading. My fear was that other web users would think the page is not done loading and have a bad experience on the web site.

I took a look at all the requests being generated with the firebug net request console and was completely blown away. It was loading tons of javascript and CSS – all things we had no use for. To top it off, it lowered my ySlow grade :)

I came up with the idea to cache the results. One of the volunteers working with the campaign I’m working on was assigned to work with me on this project: Jack Polifka. Some of the code and understanding I’m going to share here can be partially attributed to him.

What Is The Plan

The plan was to do the same request as the fanbox, but to cache that response. I thought this could be done with CURL, stored, and reloaded every hour. It wasn’t that imperative that the fan pictures and count updated every load. Once an hour would suffice.

Issues We Ran Into

Because the fanbox was being cached locally, it was destined not to work exactly perfect. The good news is that we were able to completely style it perfectly to fit in our layout (Thank our good friend Mark Skowron for his intuitive eye.) There were some issues though:

Javascript

The first issue was the use of javascript in the fanbox. Because Facebook was loading javascript from its own domain, it could do many extra functions that we wouldn’t be able to accomplish. The biggest one was identifying if you were already a fan of the page. Since Facebook was loading content from their domain, they were able to access your Facebook ID and determine if you already fan’d the page. Then, the item would update to say you’re already a fan. We couldn’t do this.

Fan… um… relativity

If you are logged into facebook when you view the widget, it appears to search the page for fans that are friends of yours. If so, it gives them priority in the picture ordering. Since we couldn’t provide that realtime cookie access, it is just a generic list of fans.

Facebook doesn’t like CURL

Honest FB, I wasn’t trying to mess with you or take advantage of you! But, when you saw me coming with a CURL user agent, you stopped me in my tracks. In order to continue the request, we had to change the CURL User Agent to something else. Then it loaded perfectly.

But, We did it Anyway

In the end, it was a success. A cron job is ran every hour to get the content of the facebook widget. Then, it is written out to a re-formatted output file and read for the next hour.

The cron script:
build_facebook_fanbox.php

1
2
3
$builder = new facebook_fanbox();
$builder->getHTML();
$builder->write();

This is pretty self explanatory. The class is instantiated. A request is made to get the content. And then it’s written out. Those steps are separate because it facilitates testing easier. It’s not always necessary to write to a file during testing.

Next, I’m going to cover parts of the class and supporting files individually.
fanbox.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class facebook_fanbox
{
	const USER_AGENT = 'Firefox XXXXXXXXXXXX';
	const FANBOX_URL = 'http://www.connect.facebook.com/connect/connect.php?api_key=xxx&channel_url=xxx&id=xxx&name=&width=280&connections=8&stream=0&logobar=1';
 
	protected $_html, $_fanCount = 0, $_fans = array(), $_pageInfo, $_output;
 
	public function getHTML()
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_USERAGENT, self::USER_AGENT);
		curl_setopt($ch, CURLOPT_URL, self::FANBOX_URL);
		curl_setopt($ch, CURLOPT_FAILONERROR, TRUE); // if 400+, error out - don't want this
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		$this->_html = curl_exec($ch);
 
		if ($this->_html === false) {
			throw new exception(curl_errno($ch) . ': ' . curl_error($ch));
		}
 
		curl_close($ch);
 
		/**
		 * write out to temp cache to review
		 */
		$file = '/tmp/fanbox.html';
		file_put_contents($file, $this->_html);
	}
}

First, there are two constants. The first USER_AGENT is just the full user agent that we use to request the content of the fanbox. Remember, it was rejecting CURL’s user agent. The other constant is the FANBOX_URL which is the entire URL that is loaded. I retrieved this by reviewing the requests in the net::console window of firebug. Yours will contain your API Key and channel information.

The getHTML() function simply opens up a connection and retrieves the HTML. If there are any errors, it fails. Finally, it writes it out to a cache file in the tmp directory. I do this just in case I want to compare later on to make sure my final output matched what I retrieved.

Moving on, I added the following method:

1
2
3
4
5
6
	public function write()
	{
		$this->_parseHTML();
		$this->_buildOutput();
		$this->_writeOutput();
	}

This is pretty self explanatory. It just calls three internal methods, which I’ll cover next.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	protected function _parseHTML()
	{
		$fancountExp = '/<span class="total">(.*?)<\/span>/';
		$fanExp ='/<div class="grid_item">(<(a|span).*?<\/(a|span)>)<\/div>/';
		$pageExp ='/<div class="connect_top clearfix">(<a.*?<\/a>)/';
 
		preg_match_all($fancountExp , $this->_html, $fanCount);
		$this->_fanCount = $fanCount[1][0];
 
		preg_match_all($fanExp, $this->_html, $fans);
		$this->_fans = $fans[1];
 
		preg_match_all($pageExp, $this->_html, $pageInfo);
		$this->_pageInfo = $pageInfo[1][0];
	}

Here, there are just three regular expressions used to parse out various bits of information from the retrieved HTML. First, the fan count. Then, all the fan boxes (which are a tags, span tags and img tags). Finally, it gathers the page information itself. This allows the owner of the page to change the page name – and our code not to break! As you can see, it assigns all of the values to the class internally. Let’s look at the next method.

1
2
3
4
5
	protected function _buildOutput()
	{
		$params = array('count'=>$this->_fanCount, 'fans'=>$this->_fans, 'page'=>$this->_pageInfo);
		$this->_output = view::get('facebook/fanboxtemplate', $params);
	}

This is pretty simple. It builds a parameter array of the values we’ve identified before. Then, this is passed into the helper function I have to generate the view. (The specifics of the helper function won’t be covered here. However, all it does is include the file specified in the first parameter, and assign all the values in the next parameter to an internal $vars array.) This output is then assigned to an internal variable.

In order to understand how we’re re-parsing the content, lets take a quick look at the stripped down HTML file. (This is a smaller version and is only meant as demonstration).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="fan_box">
<?php echo $vars['page']; ?><br />
<div class="connect_button">
<a href="#" id="FBbecomeFan" class="FBbutton FBbutton_Gray FBActionButton">
<span class="FBbutton_Text"><span class="FBbutton_Icon FBbutton_IconNoSpriteMap">
</span>Become a Fan</span>
</a>
</div>
 
<div class="connections">
<?php echo $vars['count']; ?> Fans
 
<div class="connections_grid">
<?php
foreach ($vars['fans'] as $fan) {
	$fan = str_ireplace('<img', '<img alt="facebook profile icon"', $fan);
	print $fan;
}
?>
</div>
</div>
</div>

The most notable thing about this example is that we replace the image tag from Facebook’s fanbox and add in alt text. That’s about it. It’s all pretty self explanatory. (Once again, our real production version has a lot more options in it – this is just for demonstration.)

Finally, the last function is pretty simple:

1
2
3
4
5
protected function _writeOutput()
{
	$file = APPLICATION_PATH . '/views/partials/facebookfanbox.phtml';
	file_put_contents($file, $this->_output);
}

The final processed output is written to a page that is later included.

Final Words

While I’d love to use Facebook’s built in fanbox widget, it was causing issues with our page. I couldn’t afford to have the site slowing down because of their excessive resource loading. I think this method bridges the difference nicely.

chaining methods in PHP

Wednesday, November 4th, 2009

I rarely find myself needing to chain methods in PHP – but its not an altogether bad idea. The only caveat that is necessary is that your code must be written in such a way that a method can fail, but other methods can still continue. For example, you couldn’t have one method return false… that would break the chain. You also couldn’t have a method depending on the actions of the previous method to be successful if it is allowed to legitimately fail. There is no intermediate step to check ‘is it valid to continue?’

At any rate, here is my test code that PHP method chaining works great:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class foo
{
    public function bar()
    {
        print 'bar<br />';
        return $this;
    }
 
    public function splat()
    {
        print 'splat!<br />';
        return $this;
    }
 
    public function twin()
    {
        return $this->single();
    }
 
    public function single()
    {
        print 'single<br />';
        return $this;
    }
 
    public function neato()
    {
        print 'neato';
        return $this;
    }
}
 
$ohNo = new foo();
$ohNo->bar()->splat();
$ohNo->twin()->neato();

As expected, the output is:

bar
splat!
single
neato

Each of these methods are pretty self explanatory. The only one that is slightly different is the twin()/single() methods. Note how the twin() method is returning the value of another method directly. The chain remains solid and connected.

Bluefish Editor could help you leak your PHP!

Friday, October 23rd, 2009

The BlueFish editor is a primarily linux based visual editor for various web languages. Visit the site for more…

One thing I had noticed about a few projects I was working on was the presence of files named things like:
index.php~
settings.php~

After opening them up, I noticed that they were straight PHP code. The settings file particularly was intriguing – as it had db credentials in it. These were in our deployment… so anyone who surfed to http://domain.com/settings.php~ could see our code. Not good.

I talked with the other developers to see why this was happening. The most irritating response I received was “just add it as a php mime type so we don’t have to worry about it.” Grrarr!

BlueFish OptionsTurns out it was one of the developers using the Bluefish editor – and then committing his entire working directory. Bluefish has an auto backup process that creates a backup of a file before a save. Its default setting was to use a tilde with the file name. This option is found under the Edit -> Preferences menu option.

So my suggestions are to a) be very careful what you commit, b) turn off that function if its not needed or at least c) check mark the ‘remove backup file on close’ option.

Seems to me that some clever google queries may be able to help us find this problem elsewhere out in the wild…

  • twitter loader

Follow me on twitter: @aaronsaray

The views on this website are my own and do not reflect the opinions of my employer or clients.
Creative Commons License Home | Open Source | Book | Music | Art | Bio | Resume | Contact
My Baby