Gregg Hilferding :)


Hacking Lessn To Track Statistics

When Shaun Inman shared his URL shortening PHP script Lessn, I was pretty excited.

Right now I use kl.am, a free service from Raven SEO Tools, for all my URL shortening needs. It's awesome and 99.5% perfect. But I've really wanted to host my own for three reasons:

  1. I want bulk uploads with custom short URLs so I can have predictable, product SKU short URLs.
  2. I want to be able to repoint a short URL if it's old URL changes or 404s.
  3. I don't want to compete with other users for custom URLs.

Unfortunately, rolling my own will mean I miss out on all the amazing coolness of kl.am. Mine wouldn't be as slick or easy to use. However, Lessn is slick and easy to use. But it doesn't do custom URLs and it doesn't track how often people visit a short URL. So I added those features.

Tracking clicks

In the file /-/db.php, add this statement to create a second table:

mysql_query('CREATE TABLE IF NOT EXISTS `'.DB_PREFIX.'stats` ( '.
'`id` int(11) unsigned NOT NULL auto_increment, '.
'`url_id` int(11) unsigned NOT NULL, '.
'`ts` datetime NOT NULL, '.
'`ua` text character set utf8 collate utf8_unicode_ci NOT NULL, '.
'`ref` text character set utf8 collate utf8_unicode_ci NOT NULL, '.
'`ip` int(4) unsigned NOT NULL, '.
'PRIMARY KEY (`id`), '.
'KEY `url_id` (`url_id`) '.
') ENGINE=MyISAM DEFAULT CHARSET=utf8;');

Then, in the /index.php file, add this line right after the header('Location:'); line:

mysql_query("INSERT INTO `".DB_PREFIX."stats` SET `url_id`='".$row['id']."', `ts`='".date("Y-m-d H:i:s")."', `ua`='".mysql_real_escape_string($_SERVER['HTTP_USER_AGENT'])."', `ref`='".mysql_real_escape_string($_SERVER['HTTP_REFERER'])."', `ip`='".sprintf("%u", ip2long($_SERVER['REMOTE_ADDR']))."' ");

To test, go visit one of your short URLs, and then go check the stats table. You should see a new entry in the table.

Now, how you choose to display the data from this table is up to you. :)

Published by Gregg Hilferding on August 31st, 2009 at 1:31 pm. Filed under PHPNo Comments

Introducing expandUrl, redirect resolution for the rest of us

To solve a problem I describe in detail below, I recently created a new site called expandUrl. The expandUrl™ service does a few things very well:

  • Tells you the "real" URL of any shortened URL (regardless of what service provides it).
  • Gives technical types the redirect codes involved in all the redirects for any particular link.
  • Discovers both canonical and shorturl data for the final expanded URL.
  • Offers a fast API for quickly finding the expanded URL (and a less fast API for getting the same detailed data that is available from the web interface).

Heard enough? Go check it out:

expandurl

The expandUrl™ site was built to fix a specific problem I recently discovered on one of my other sites, so you can be confident that expandUrl™ has been tested on a sample set of a couple thousand real-life URLs. However, the total number of hours I've spent building expandUrl™ can still be counted on one hand so you are bound to discover some edge cases which I haven't encountered.

I appreciate all bug reports to be posted below. Security concerns should also be posted here, but I will pre-moderate those until they are fixed. (Not to hide them, but to give me the opportunity to fix them before they are exploited.)

Eating my own dog food

I run a site which aggregates RSS feeds from a few dozen different sources. One of the included sites accidentally let their domain name expire and had to move to a new domain name. This particular site also hosted their RSS feed with Feedburner.

When the site changed domain all the URLs were broken. You may be thinking, "Well, duh. There's nothing you can do to fix that." In this scenario however, the original site owner simply changed domain names. All of the old URLs would map perfectly to the new domain name:

Old: http://example.com/blog/2007/04/post.html

New: http://example.org/blog/2007/04/post.html

If I had stored the actual URLs, I could simply do a REPLACE in the database. However, since I stored the Feedburner URLs in my database, it would be impossible for me to do so. That's because Feedburner obfuscates the actual URL data:

Actual URL: http://example.com/blog/2007/04/post.html

Feedburner URL: http://feeds.feedburner.com/~r/exampleblog/epRL/~3/745213668/post.html

I quickly built expandUrl so that I could resolve any URL (be it a short url, tracking script, or feed redirect) to the actual URL. I then ran an automated script to determine the relative link on the old domain, replaced the old domain with the new domain, and updated the database accordingly. An hour or so later, and all those posts are now fixed.

I ran this script against the entire database of URLs to go ahead and resolve any other redirects found. I made an interesting discovery. Many of the RSS feed services will generate multiple redirect URLs for the same blog post permalink. My database had a UNIQUE index on the URL field as a means of preventing the same post from showing up multiple times, but it turned out the feed services were inadvertently circumventing that "protection."

In the end, integrating expandUrl has cleaned over 300 duplicate URLs from my database and helped improve the future maintainability of the link data the site has collected.

If you find expandUrl™ helpful, I'd love to hear about it! :)

Published by Gregg Hilferding on April 27th, 2009 at 7:30 am. Filed under PHP,SEO,Signal,Social Media,Webmaster2 Comments

Guess a number between 1 and 100 in 7 or fewer tries

A number is randomly chosen between 1 and 100. The "guesser" is told at each guess whether their guess is correct, too high, or too low.

Four methods tested with the sequence used to successfully guess the number "1."

Random Starts with a random number and draws a random guess from the range implied by the results of each round.
Guessing: 65, 41, 32, 18, 14, 9, 3, 1

Equal Division Starts with 50 and each time guesses the median value from the range implied by the results of each round.
Guessing: 50, 25, 13, 7, 4, 2, 1

Fibonacci Division Starts with 50 and each time guesses the golden ratio value from the range implied by the results of each round. In other words, divides by 1.618033989 instead of by 2. So either 32 or 81 will be it's second round guess.
Guessing: 50, 32, 21, 14, 10, 7, 5, 4, 3, 2, 1

Fibonacci Offset Division Starts with 50 and each time guesses the golden ratio value based on whether they were too high or too low. So a guess of 50 that was "Too Low" would then choose 81. A guess of 50 that was "Too High" would choose 19.
Guessing: 50, 19, 7, 3, 1

I expected that Equal Division would be the winner but wanted to play around with some other options just to see. Here's the distribution of how many tries it took to guess the correct number over 500 iterations using each of the different methods.

The condition for a win is guessing in fewer tries than the base-2 logarithm of the range (in this case the range is 100 and the base-2 logarithm of 100 is 6).

Number Guesser Result Charts

Published by Gregg Hilferding on February 2nd, 2007 at 5:09 pm. Filed under PHP,SignalNo Comments

Multiple Transparent Borders

Multiple Borders Example

When drawing a text string that has multiple text borders like, for example, the family reunion t-shirt design here, you have unique issues building the multiple layers of borders.

Depending on how you draw multiple borders you can get many different effects:

What's the difference between these different approaches?

Multiple Border Tests

Answers

In option C, each letter is individually drawn starting at the largest border then working inwards to the fill. When drawing arced text, each letter is drawn in a different position so you have to draw each letter separately.

In option B, each border is drawn with a transparent fill then the separate layers are flattened.

In option A, the entire string is drawn starting at the largest border then working inwards to the fill.

Published by Gregg Hilferding on February 1st, 2007 at 4:01 pm. Filed under PHP,SignalNo Comments