My Blog

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

How I test email recipients when I develop

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];
}

How to handle AJAX errors with jQuery

August 24th, 2010

Many times, the ‘error’ property of the jQuery AJAX call is ignored. Most often, you’ll see just references to the success portion.

The error attribute of the $.ajax() is a callback – and receives three parameters. These are the XMLHttpRequest with the error, a type of error, and an error object, if one is thrown. For the most part, the first two are the only parts.

Now, the error attribute should be used for actual errors, not logical errors. For example, if you are making an AJAX call to log in the current user, and the user does not exist, this should return a success type message instead of some sort of error. Errors are things like 404′s for the AJAX call, or other HTTP issues. In fact, there are 4 types of errors that will be returned: Error – which is an HTTP error, parseerror – which is an xml/json parsing issue, timeout – which is a script that didn’t respond fast enough, and not modified.

I wrote a generic function to handle the errors. This could be name spaced I suppose or added to your standard library. In this case, it just alerts the error. (On some other sites, I generate a new modal box.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function ajaxError(request, type, errorThrown)
{
	var message = "There was an error with the AJAX request.\n";
	switch (type) {
		case 'timeout':
			message += "The request timed out.";
			break;
		case 'notmodified':
			message += "The request was not modified but was not retrieved from the cache.";
			break;
		case 'parseerror':
			message += "XML/Json format is bad.";
			break;
		default:
			message += "HTTP Error (" + request.status + " " + request.statusText + ").";
	}
	message += "\n";
	alert(message);
}

In this function, an error message is generated based on the error type. The only error that gets extra information is the default type – which is ‘error’. It then retrieves the HTTP Status code and the Status Text.

Here is an example of this in use:
ajax.php

1
2
<?php
header('HTTP/1.1 503 Service Unavailable');

And, here is the test page. When the user clicks the xx link, it will generate a request to ajax.php. This will generate a 503 error and the error handler will take over.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
	<head>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
	</head>
	<body>
		<a href="#" id="test">xx</a>
		<script type="text/javascript">
			$(function(){
				$("#test").click(function(){
					$.ajax({
						url: "ajax.php",
						success: function(){
							alert('retrieved');
						},
						error: ajaxError
					});
 
					return false;
				});
			});
		</script>
	</body>
</html>

Easy MCrypt encryption class

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);

SimplePHPMailer: now with required fields

August 10th, 2010

SimplePHPMailer – my open source plug-n-play emailing script for PHP newbs – has been updated. It now has required fields – no more blank submissions! Check it out here http://aaronsaray.com/blog/2008/04/24/simplephpmailer/ with the updated download link.

Hide Email Addresses while still using mailto: Header Redirect

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

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. :)

A Better Milwaukee

July 22nd, 2010

I know – this is a bit non standard of a blog post for me… but bear with me. :)

A fellow kayaker took this picture of Milwaukee.

I thought it could look a little better.

I’ve also attached the PSD file. If you have any suggestions on how I could have done this better/faster, please let me know. :) I’m still trying to learn how to touch up photos better.

Using Google Charts to make QR Codes

July 13th, 2010

QR Code for this page

QR Code for this page

Google Charts is my hero yet again. This time, I happened to notice that they have a chart in their API for QR Codes. Considering I was just searching google for a PHP class to do this, I was pretty ecstatic.

To implement, I made a quick line of jQuery to generate my QR Codes. Of course, I did this after the page loaded :) My goal was to generate a QR code for the page that the user is currently viewing. Pretty simple:

1
$("#qrImage").attr('src', 'http://chart.apis.google.com/chart?chs=150x150&cht=qr&chl=' + escape(window.location.href) + '&choe=UTF-8');

You can find all of the details and other parameters here: http://code.google.com/apis/chart/docs/gallery/qr_codes.html

My NonDisclosure Promise

July 6th, 2010

As you may remember, I wrote a piece about why I do not sign any NDAs here. I’ve had some time to reflect on that entry, and I want to move forward. I want to give any client I work with my new “promise” – the Non Disclosure Promise. Let me tell you a bit more why I choose not to sign NDAs, and what I can offer you instead.

Open Source Licensing

I specialize in PHP. PHP is open source. There are still some licensing requirements with PHP, however. To use the most standard features and create my own code, I’m bound to only PHP’s license. This allows me to create code for my clients and allows them to have full rights to the pre-processed source.

There are times, however, when it’s not necessary to reinvent the wheel (see: code things from scratch). This is where the Open Source philosophy of PHP flourishes. There are tons of scripts and code snippets that are available open source and free. However, they may have different licenses. Some licenses even require that the source code that you modify be contributed back to the project (or even worse – BSD’s license which requires the copy right text in the code). Because of this, it is possible that a library is chosen that I have to slightly modify for the client’s project. Depending on the licensing, I may have to submit the changes back to the project.

Idea vs Implementation

I touched on this heavily in my last piece, but I just wanted to refresh: There are a million ideas – success comes from the proper implementation of said ideas. I’ve seen it so many times that an Entrepreneur comes across an idea that they think is ground breaking – and clutches to that like no tomorrow. There are tons of cool idea companies that failed (see: dot com bust. see: 200x recession). It’s all in the implementation. Suggesting that a programmer legally bind themselves to not talk about a specific idea in the open source world is akin to death – and here’s why:

Two heads are better than one

Sometimes it can be useful to talk a solution over with a trusted colleague or friend. I can’t tell you how many times I’ve created a User Interface and then turned to my trusted doctor friend and asked her to use it. When she stares at me blankly, I know I have more work to do.

I’m not the smartest guy alive

I know – you didn’t think I’d say it. But it is true! I have some friends and peers that are better suited at specific technologies than I am. For example, if I were making a PHP/Java bridge, I would have to consult with some of my Java buddies. If I couldn’t at least explain a portion of my “problem” to them, they will not be able to help me. And that help produces a better product and saves the client money.

My Non Disclosure Promise

With all this explanation done, I want to tell you what I can do.

I promise to program ethically

I will never take, sell or abuse the technology and proprietary ideas that the client has entrusted me with. Besides, this is like shooting myself in the foot. Trust and responsibility are key in this industry. If I started doing things in-ethically, it would be the death of my career.

I will try my hardest to protect your investment

There are times when the ‘secret’ gets shared – even with the NDAs that programmers sign. However, I’ve ran a business and participated in many other. I know there are more intangible resources in the business than anyone can calculate. I recognize the hard work, and will do my best to both solve any issues, program any solutions and share only what would be needed to further the project.

I will communicate

In the absence of the NDA, I have a certain liberty to speak with colleagues about the project. However, if there is something that I think I may want to discuss with someone but think it may be infringing on what the client may consider proprietary business matter, I will communicate both the what I want to communicate and the why I want to talk with someone about this. I will also present alternatives if that is an uncomfortable choice for the client.

Is this good enough?

Now, I have both a decision not to sign any more legally binding NDA documents as well as an official public promise – one I take dearly serious – one that is tied to my name – I give my word. I hope this is honesty is enough for potential clients to understand and trust me for future projects.

Book Review: Expert PHP 5 Tools

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.

  • 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