PHP Tag Cloud Remove Common Words

July 11th, 2008

If you have an application that allows a user to enter arbitrary tags for an entity, you might want to filter their tag input based on certain criteria. This tutorial assumes that you will be working with a space-separated list of tags, e.g. from a form input field. If your tag input is coming from an array, you can try $tags = implode(' ', $tag_array); to prepare it for the rest of the code presented here.

Firstly, we’ll cover the tag filter. A list of the most common English words was compiled by merging data from the following resources:
http://en.wikipedia.org/wiki/Most_common_words_in_English
http://www.askoxford.com/oec/mainpage/oec02/?view=uk
http://esl.about.com/library/vocabulary/bl1000_list1.htm
http://www.deafandblind.com/word_frequency.htm

We can look at the whole list later for reference, but to make an effective tag filter, many had to be removed by hand. Additionally, this script disregards words with less than three letters, so those were removed as well. Many search applications, including the one that inspired this coding, won’t mess with those. Here is a space-separated list of the common English-language words to be filtered:

able about after again all also and any are bad been before being between but came can cause change come could did differ different does don't down each end even every far few for form found four from get good great had has have her here him his how into its just keep let many may might more most much must near need never new next not now off one only other our out over part put said same say seem set should side some still such take than that the their them then there these they thing this three through too two upon use very was way went were what when where which while who will with would you your

Let’s use the following example for a space-separated tag string:

fluffy is freeze you Rocket don't cute boulder fry

The following PHP code will filter out the common words as well as words that contain less than three letters. It only pulls strings with alpha characters, and additionally converts all tags to lowercase:

$tag_filter = array('able', 'about', 'after', 'again', 'all', 'also', 'and', 'any', 'are', 'bad', 'been', 'before', 'being', 'between', 'but', 'came', 'can', 'cause', 'change', 'come', 'could', 'did', 'differ', 'different', 'does', 'don', 'down', 'each', 'end', 'even', 'every', 'far', 'few', 'for', 'form', 'found', 'four', 'from', 'get', 'good', 'great', 'had', 'has', 'have', 'her', 'here', 'him', 'his', 'how', 'into', 'its', 'just', 'keep', 'let', 'many', 'may', 'might', 'more', 'most', 'much', 'must', 'near', 'need', 'never', 'new', 'next', 'not', 'now', 'off', 'one', 'only', 'other', 'our', 'out', 'over', 'part', 'put', 'said', 'same', 'say', 'seem', 'set', 'should', 'side', 'some', 'still', 'such', 'take', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'thing', 'this', 'three', 'through', 'too', 'two', 'upon', 'use', 'very', 'was', 'way', 'went', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with', 'would', 'you', 'your', );

$tags = 'fluffy is freeze you rocket don\'t cute boulder fry';
preg_match_all('/([a-zA-Z]{3,})/', $tags, $matches);
$matches[0] = array_map('strtolower', $matches[0]);
$tags = array_diff($matches[0], $tag_filter);

The $tags array would then be filtered and lowercased, producing the following output with print_r($tags):

Array
(
    [0] => fluffy
    [1] => freeze
    [3] => rocket
    [5] => cute
    [6] => boulder
    [7] => fry
)

If you need to convert it from the array back to a space-separated string, try $tags = implode(' ', $tags);. You may also of course add more words to the word list — that could come in handy with other application-specific functions such as cursing filters.

Here is the full list of common words merged from the above-stated resources, separated by spaces, including those with less than three letters:

a able about act add after again air all also am an and animal answer any are as ask at back bad be been before being between big boy build but by call came can case cause change child city close come company could country cover cross day did differ different do does don't down draw each early earth end even every eye fact far farm father feel few find first follow food for form found four from get give go good government great group grow had hand hard has have he head help her here high him his home hot house how i if important in into is it its just keep kind know land large last late learn leave left let life light like line little live long look low made make man many may me mean men might more most mother move mr mrs much must my name near need never new next night no north not now number of off office old on one only or other our out over own page part people person picture place plant play point port press problem public put read real right round run said same saw say school sea see seem self sentence set she should show side small so some sound spell stand start state still story study such sun take tell than that the their them then there these they thing think this thought three through time to too tree try turn two under up upon us use very want was water way we week well went were what when where which while who why will with woman word work world would write year you young your

Age From Birthday Calculator

June 24th, 2008

Figured this might be handy to some folks… Just enter in a birthdate (or just any date for that matter, doesn’t necessarily have to be a birthday), and it will give you the number of years that have passed since then! Why use your head when we have computers!?

Month:

Day:

Year:
(YYYY)

UNIMPORTANT NOTE: This script uses the average number of days in a year as part of its calculation. If I hear about that causing any problems I’ll look into another way of doing it, but I think it’s the most reasonable, far more so than using a whole number like 365.

The JavaScript:

function getAgeFromBday() {
  var month = document.getElementById('bdc-month').value;
  var day   = document.getElementById('bdc-day').value;
  var year  = document.getElementById('bdc-year').value;
  var bdate = new Date(year, month, day);

  if (bdate.getDate() != day || bdate.getMonth() != month || bdate.getFullYear() != year) {
    alert('That date appears to be invalid!');
    return false;
  }

  var today = new Date();
  today.setHours(0);
  today.setMinutes(0);
  today.setSeconds(0);

  if (bdate > today) {
    alert('Provided date must fall before today\'s date!');
    return false;
  }

  alert(Math.floor((today - bdate) / 31556952000));
}

The markup:

<p>
  <strong>Month:</strong><br />
  <select name="bdc-month" id="bdc-month">
    <option value="0">January</option>
    <option value="1">February</option>
    <option value="2">March</option>
    <option value="3">April</option>
    <option value="4">May</option>
    <option value="5">June</option>
    <option value="6">July</option>
    <option value="7">August</option>
    <option value="8">September</option>
    <option value="9">October</option>
    <option value="10">November</option>
    <option value="11">December</option>
  </select>
</p>
<p>
  <strong>Day:</strong><br />
  <input name="bdc-day" id="bdc-day" style="width: 15px;" type="text" />
</p>
<p>
  <strong>Year:</strong><br />
  <input name="bdc-year" id="bdc-year" style="width: 30px;" type="text" /> (YYYY)
</p>
<p>
  <button type="submit" onclick="getAgeFromBday()"><strong>DO IT</strong></button>
</p>

Drumstick spinning / twirling techniques

June 11th, 2008

This video covers only the first technique described; it is the one about which people are generally most curious. I might record some other videos that cover the other ones mentioned — and also some not mentioned — depending on how this one goes and also how motivated I might be feeling to do more.

Shortnin’ Bread - Traditional

May 18th, 2008

New recording

Put on the skillet, put on the lid
Mama gonna bake a little shortnin’ bread

DOWNLOAD (1.4MB; 1:31)

The chords and lyrics were from here, but transposed to C and I used a 7th for the V chord (so C and G7 instead of G and D, respectively). Instruments used were ukulele, mandolin, and shaker.

Background info on the song.

Peter, Paul, and Mary - River of Jordan Chords and Lyrics

March 18th, 2008
  E            E/F#         E           E/F#
I traveled the banks of the River of Jordan
   E             E/F#         F#
To find where it flows to the sea.
  F#m           F#m/G#      F#m             F#m/G#
I looked in the eyes of the cold and the hungry
      F#m       F#m/G#     F#m
And I saw I was looking at me.
A                   F#m
I wanted to know if life had a purpose
    A                        B7
And what it all means in the end.
       E         E/F#        E        E/F#
In the silence I listened to voices inside me
         E        E/F#      E
And they told me again and again.

       E/F#     B7                       E    E/F#  E
The is only one river. There is only one sea.
       E/F#          B7                        E   E/F#  E
And it flows through you, and it flows through me.
                  C#m
There is only one people. We are one and the same.
               F#7                    B7
We are all one spirit. We are all one name.
           E      E/F# E         E/F#         E    E/F#  E
We are the father,       mother, daughter and son.
         E/F#        F#7          B7
From the dawn of creation, we are one.
       E    E/F#  E
We are one.

Every blade of grass on the mountain
Every drop in the sea
Every cry of a newborn baby
Every prayer to be free
Every hope at the end of a rainbow
Every song ever sung
Is a part of the family of woman and man
And that means everyone.

We are only one river. We are only one sea.
And it flows through you, and it flows through me.
We are only one people. We are one and the same.
We are all one spirit. We are all one name.
We are the father, mother, daughter and son
From the dawn of creation, we are one.
We are one.

Chord fingering suggestions

       (Standard tuning)
          E A D G B E
          -----------
E        [0 2 2 1 0 0]
E/F#     [2 2 2 1 0 0]
F#       [2 4 4 3 2 2]
F#m      [2 4 4 2 2 2]
F#m/G#   [4 4 4 2 2 2]
A        [0 0 2 2 2 0]
B7       [x 2 1 2 0 2]
C#m      [x 4 6 6 5 4]

I believe this is the only place on the web you’ll find chords for this song. If you find another, I’d be curious to see it (I looked quite a bit). I think this is a reasonably accurate transcription, it’ll surely get you through. For the last two stanzas, just play the chords from the first two and it should fit well enough.

The inspiration for transcribing it came on a pep band CAA bus trip, watching a scene in the movie Airplane!, where Randy the flight attendant plays it to Lisa, the girl on the plane who had fallen ill.

We Are Siamese (If You Please) - Lady and the Tramp, Chords and Lyrics

February 16th, 2008

According to the IMDB soundtrack listing for Lady and Tramp, the actual name of the song is just “Siamese Cat Song” and it’s by Sonny Burke & Peggy Lee.

The chord transcription may not be perfect, but I believe that it is at least close, and it certainly seems to work with the vocals. Feel free to correct me if I’m wrong here.

A
We are Siamese if you please
                      Cm
We are Siamese if you don't please
Cm
Now we lookin' over our new domicile
E                            A
If we like we stay for maybe quite a while

A
Do you seeing that thing swimming round and round? (Yesss)
                               Cm
Maybe we could reachin' in and make it drown (Meow)
Cm
If we sneaking up upon it carefully
E                               A
There will be a head for you, a tail for me

A
Do you hear what I hear? A baby cry
                                Cm
Where we finding baby there are milk near by
Cm
If we look in baby buggy there could be
E                            A
Plenty milk for you and also some for me

The original recording is in the key of Bb, so if you want to play in the original key using these same chord shapes, just capo the 1st fret. You can listen to my recording of this song from the archives, but note that I added extra chord changes that aren’t in this transcription.

Using Web Developer Toolbar with Firefox 3 Beta

January 24th, 2008

THE UPDATES KEEP COMING! The toolbar has been updated for RC1, so be sure to grab it from Firefox addons, or the official site.

YET ANOTHER UPDATE: Firefox 3 Release Candidate 1 (RC1) is out, so this article once again applies. Have at it! I’ll post another update when the toolbar is updated again and works out-of-the-box.

IMPORTANT UPDATE: The toolbar has been updated and should now work with Firefox up to beta 4. You can download it from Firefox addons, or the official site. Thanks Davosian for the heads up!

So I recently upgraded to Firefox 3 Beta 3, and am now using it as my primary browser. Major speed increase with version 3! Memory seems to be handled much better! Excellent!

Note: At the time of this writing, you can get Beta 2 from here. I went ahead and got a nightly build, which, at the time of this writing, is beta 3. Regardless of which beta version you choose, know that you are taking a risk as the software is not in its final stages.

If you are a web developer, you probably use Chris Pederick’s impeccable Web Developer toolbar.

It hasn’t yet been updated for beta 3! Why? Probably because it hasn’t been tested enough! If you are like me and are hardly concerned about that, and want your Web Developer toolbar, then you might be interested in knowing how to get it working with beta 3!

The steps:

  1. Download the Web Developer toolbar .xpi file from the official site. NOTE: If you are doing this from Firefox, you must right-click and choose “Save Link As…” (otherwise it will attempt to install the extension)
  2. Rename web-developer.xpi to web-developer.zip. (XPI files are just ZIP files with different extensions)
  3. Open the zip file, extract and open the file named install.rdf.
  4. At the time of this writing, the line we are looking for is line 27, and it should look something like this:
    <em:maxVersion>2.0.0.*</em:maxVersion>.
  5. Change it to look something like this:
    <em:maxVersion>4.0.0.*</em:maxVersion>.
  6. Save the file and add it back to the zip file. Depending on your zip utility, this can usually be accomplished by opening the zip file and dragging the newly saved file back into it (be sure to overwrite the existing install.rdf). Rename the file to once again have an .xpi extension.
  7. Open Firefox and drag web-developer.xpi into the browser window. It should then ask you if you want to install the extension, and it should not complain about the FF version!

That’s it! This is working fine for me so far, but don’t come crying to me if your computer blows up…

UPDATE: Thanks Ronnie for pointing out the secure updates issue. I forgot to mention that. Check this comment for direction on getting around that warning. Please let me know if those instructions aren’t clear enough and I’ll update again.

ANOTHER UPDATE: The Edit CSS feature (which, to me, is one of the most important) seems to be glitching. It has worked, but doesn’t always. One persistent problem is with closing the Edit CSS dialog once it’s opened — it doesn’t close once it’s opened. So if you have 6 trillion tabs open and you want to edit the CSS on one of the pages… WAIT! Open a new window and then do it… That way once you’re finished, you don’t lose all your tabs.

.htaccess Add Trailing Slash to URL

January 11th, 2008

If you are rewriting your URLs using .htaccess and mod_rewrite, you might run into a problem where trailing slashes are ignored.

Here is an example scenario. Say you have PHP script that takes query arguments like this (replace example.org with your domain, of course):

http://example.org/index.php?page=content&title=welcome

You are rewriting it to look like this:

http://example.org/content/welcome/

For reference, here’s an example .htaccess script that would do that (there are plenty of other ways, this is one basic example):

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]

And here is the problem with that! If you try a URL like this (without the trailing slash):

http://example.org/content/welcome

404! OH NOES!!

So, one alternative is to change the .htaccess directive to look like this:

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/?$ /index.php?page=$1&title=$2 [QSA,L]

So now both of these URLs work!

http://example.org/content/welcome/, AND
http://example.org/content/welcome

BUT WAIT! Trailing slashes are prettier, and we don’t want two separate URLs for the same content! Google doesn’t even like that! OOOH NOOOES!!

How to force a trailing slash in the URL:

  • Make your rewrites require the trailing slash, as with the first example:
    RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]
  • Add this code below your regular rewrites:
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule (.*)$ http://example.org/$1/ [R=301,L]

Note: This should work no matter how your application handles URLs. Some applications, however, such as WordPress, pull in the entire URL query regardless of slashes. If your application inherently does not care about trailing slashes in its RewriteRules, then you must put these directives before your other rewrites. The reason for that is because if it goes after them, then the application’s own RewriteRule will go into effect, accepting the URL without the trailing slash, and will likely ignore any subsequent rules (because of the [L] flag). The reason I suggest in this tutorial to put the script after your normal rewrites is for optimization and that can only apply when your normal rewrites require a trailing slash. If the URL has a trailing slash, as is the desired scenario, then it should go ahead and apply the rules and not even bother looking at the redirection directive.

Another point to keep in mind is that this script will redirect and add a trailing slash even for bogus URLs. E.g. - if you type this hypothetical non-existent URL into the address bar:

http://example.org/garhtrehstreetreetree

It is going to rewrite and forward it to the following URL with a trailing slash:

http://example.org/garhtrehstreetreetree/

Before displaying the expected 404 error. Note that this is not typical behaviour of Apache server. However; I do not consider this quirk to be a big deal at all, and I believe that it is unavoidable since we are handling this with .htaccess.

Explanation of the directives and regular expressions:

The two RewriteCond lines test to make sure the requested filename is not an actual file or directory, respectively. This makes sense because we’re rewriting URLs here; the requested URLs are not actual files, they are references to actual files and query arguments.

The regular expression in the third RewriteCond does two things. It checks to make sure the requested “filename” does not have an extension. If for whatever reason it has an extension, then we probably don’t want to add a trailing slash! Note: This expression considers an extension anything that has a period followed by between 1 and 5 letters and numbers. That covers your .js and your .phtml, at least. Feel free to change the 1 and 5 to suit your application. The other thing the third RewriteCond does is make sure that the URL doesn’t already have a trailing slash. If it didn’t do that check, the redirect could attempt to loop forever and that’s annoying!

The last line, the RewriteRule, will capture the entire URL path. It then does a 301 redirect to the same URL path with a trailing slash appended.

IMPORTANT UPDATE (IMPORTANT UPDATE TO IMPORTANT UPDATE: IMPORTANT UPDATE NOW IRRELEVANT — SEE EVEN MORE IMPORTANT UPDATE): It appears that some configurations do not support lookaheads in regular expressions! I have a strong suspicion but do not know for sure that the issue is the Apache version. This tutorial should work fine for Apache 2, and may not work for Apache 1.3. That, specifically, was the case with the configurations I tested. Here is an alternative route if you are getting internal server errors with the original method:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)([^/])$ http://example.org/$1$2/ [R=301,L]

I do not think it is as pretty, but ya gotta do what ya gotta do!

EVEN MORE IMPORTANT UPDATE: After browsing the web for a minute, I came across another site that has a similar (but different) tutorial. After looking at the example therein, I metaphorically smacked my forehead and realized that a very small part of the original example (specifically, the lookahead ((?!/)) part) was not necessary, and it now works on all tested servers. The tutorial has been updated to reflect this change and is now even prettier than it was before. Thank you other person, I feel dumb now and hats off to you.

Canon PowerShot SX100IS

December 14th, 2007

The Canon PowerShot SX100IS is a good digital camera. A few years ago I had a PowerShot A60 — definitely money well-spent, loved the heck out of it. Unfortunately the construction of the A60 was crap and it ended up breaking, but it was a trooper while it lasted. At one point Canon did put out a CCD Image Sensor Advisory Service Notice, and fixed the problem out of warranty along with fixing a faulty review button! This was free, and it was years out of warranty! So I think that speaks volumes about the company’s customer service.

Back to the PowerShot SX100IS. While it’s not suitable for professional work, it does a fine job at point-and-shoot, and it goes fully manual: The ISO speed, aperture, shutter speed, flash output, white balance, and many other manual controls can be adjusted.

Low-light conditions may prove to be an obstacle with this one. While it is possible to take the ISO speed to 1600, it’s practically pointless. There will so much noise that the picture will likely be useless.

One interesting feature that’s more of a novelty for me is the face recognition. The thing can actually spot people’s faces! While I’ll probably never use this in practice, it’s just neat!

UPDATE: Some reviews on the web say that the SX100 has a really bad video capturing system. I disagree; there is a “low quality” mode that uses a high compression suitable for email. Using that setting your video will be of poor quality. It can do higher resolution and it does a decent job. It is true that you can hear the zoom motor while capturing video. It’s not that loud, and if you’re recording something loud such as a concert, it shouldn’t be a problem. Keep in mind that this is not a digital video camera anyway, it’s a point-and-shoot, and it does a good job with what it’s designed to do.

Tip: I think that the Night Snapshot mode works fairly well indoors with no flash if the room has ample lighting.

Tip #2: “Dragging the shutter.” While in Program (P) mode or Aperture Priority (Av) mode, if you press the Menu button and scroll through the options on the first page, you’ll notice one called “Slow Synchro.” That creates really interesting effects. With the flash open and on in those modes, the shutter will remain open shortly after the flash goes off. In low-light situations this helps to illuminate the subject (through the flash) while also bringing light to the background (through the shutter delay). Camera shake will cause the background to blur significantly.

Moola Invitations

November 11th, 2007

Are you looking for a free invitation to Moola.com?

CLICK HERE to get your invites!

UPDATE: I got bored of Moola.com. It is so incredibly frustrating. I’m not sure whether or not I can trust it. There are a bunch of calculators out there that tell you the best possible statistical moves. Especially with those, it seems I should have been winning more than I was. I mean not everyone on that site could possibly be a freakin’ Moola master. Maybe it’s rigged! If you use the search a lot, you can rack up some money. Even that almost always lands on a minuscule value. I give up on Moola.com! If you still want to try it, you are more than welcome to have at it!