AJAX: A Beginning Tutorial – Part 1

It’s been a while since I’ve posted anything up here, so I figured it’s high time for something new.  In this case…Ajax!

Now, Ajax has a tendency to appear intimidating to people who have never programmed anything in it, and it sure looks like that would be the case.  The reality is however, that it is extremely simple to use.  You don’t even really have to learn anything other than PHP (or ASP, or whatever) and Javascript.  If you know how to build a webpage, you probably already know how to use Ajax.

There’s two main ways that I use Ajax, and they will be explained in Part 1 and Part 2 of this tutorial.  Part 1 is going to focus on what I think should be the main use of Ajax, namely returning either a very small part of data, or returning HTML in the Ajax request itself.  The second part will be focusing on returning JSON objects, and doing processing on the objects browser side.

Simple Ajax Data

One of the easiest ways to get introduced to Ajax is by returning simple data.  By “simple data” I mean something like a number, or a status message, or something of the like.  I happen to use this in one of the apps I created for returning a live total for an invoice.  While the processing is done server side, the only thing the Ajax has to worry about is the actual number.  I take the returned number, and replace a div’s content with that.  And wha-la, a working ajax script.  Lets take a look.

PHP

Due to myself being extremely non-creative in matters like this, we’re going to simply update a section of a webpage with the server’s date and time.  So our PHP script simply needs to echo that, nothing more, nothing less.

  1. <?php
  2. echo date('F j Y, g:i:s A');
  3. ?>

Well you can’t get much simpler PHP than that :)

HTML

Well, now that we have a script outputting what we want, we’re going to need somewhere to put the new information.  For our example a simple div will do wonders.

  1. <div id="ajax_time_update"></div>

We’ll need to put the div wherever in our webpage we want the time to go of course.  the only thing of interest here is that our ID must be unique, and we need to know it to reference it.

JavaScript

Javascript… This is where the magic of Ajax happens.  Let me introduce the XMLHttpRequest object, your new best friend :) .  Actually getting a hold of the object can be kind of tricky depending on what browser the user is using, so this snippet from W3Schools is invaluable.

  1. function GetXmlHttpObject()
  2. {
  3. if (window.XMLHttpRequest)
  4.   {
  5.   // code for IE7+, Firefox, Chrome, Opera, Safari
  6.   return new XMLHttpRequest();
  7.   }
  8. if (window.ActiveXObject)
  9.   {
  10.   // code for IE6, IE5
  11.   return new ActiveXObject("Microsoft.XMLHTTP");
  12.   }
  13. return null;
  14. }

After you have that in your .js file (which I recommend all ajax code going into a .js… While it really doesn’t matter, it just seems cleaner to me.), the only thing left is to grab the data.  We’ll call our function…UpdateDate (again, not very creative).

  1. var ajaxUpdateDate;
  2.  
  3. // Initial function to kick off the update
  4. function UpdateDate() {
  5.   // Lets get a ajax object
  6.   ajaxUpdateDate = GetXmlHttpObject();
  7.  
  8.   // Set our URL to our PHP file
  9.   // Although the sid parameter doesn't do anything with the script, it stops
  10.   // apache from caching the result
  11.   var url = "http://www.thechikun.com/ajax/ajtut/part1-getdate.php?sid=" + Math.floor(Math.random()*30001);
  12.  
  13.   // Set the function that will be called when the request is complete
  14.   ajaxUpdateDate.onreadystatechange=UpdateDateCallBack;
  15.   // Tell the object that we want to use the GET method, our URL, and to do it asynchronously
  16.   ajaxUpdateDate.open("GET", url, true);
  17.   // Send the request, and since we have nothing else other than the URL to send…
  18.   ajaxUpdateDate.send(null);
  19. } 
  20.  
  21. // Callback function to modify the page
  22. function UpdateDateCallBack() {
  23.   // First, lets check if we're on ready state 4, which is done
  24.   if(ajaxUpdateDate.readyState == 4) {
  25.     // Grab the handle to the div we want to update, by the div's ID
  26.     var datediv = document.getElementById("ajax_time_update");
  27.     // Set the content of the div to be our returned value
  28.     datediv.innerHTML = ajaxUpdateDate.responseText;
  29.   }
  30. }

Now, put all 3 functions (don’t forget the global varibale ajaxUpdateDate!!!) into a .js file, and include that with your HTML, like this:

  1. <script type="text/javascript" src="http://www.thechikun.com/ajax/ajtut/part1-getdate.js">
  2. </script>

The only thing we’re missing to call this complete is just a way to fire it off.  Well thats easy enough, we’ll have a button that when you click it, calls the JavaScript function UpdateDate().

  1. <input type="button" onclick="UpdateDate();" value="Check Time" />

If you’ve been following along correctly, this should be the end result.

...

Ok, so I cheated right there and used a pre instead of a div, but only cause i like the formatting :P .  This is pretty much it for part 1; part 2 is coming soon with JSON object manipulation.

ChikunCount – WordPress Plugin

Well, I haven’t blogged anything on here for a while, mainly because I’ve been working on ChikunCount, the first WordPress plugin (WordPress anything really) I’ve worked on.  I was actually pleasantly surprised at how easy it is to do development work for WP.  So, in the spirit of keeping this a technical blog, I’ll go through some of the key points of ChikunCount, and how it does what it does.

Logging a pageview

Now, the hit counter that I’ve created in all honesty isn’t the most advanced hit counter ever.  All it does is log when a page gets viewed.  I could expand upon it by checking the times of last hit or session management, and separate page views from visits, but I’m kind of lazy, and I’ve never needed to do that in the past, so it is what it is : ).

So, in WordPress development, you register functions with the WP engine so it will run your functions at the specified time.  I use the “get_header” action that the template calls, because if we’re requesting the header, we’re actually viewing the page.  Figured it would be a good place to log the hit.  In ChikunCount this is accomplished with:

  1. add_action('get_header', '_chikuncount_updatehits');

This code is placed in the main body of the php file, and is thus executed when the WP engine includes my plugin file.  The first argument ‘get_header’ is the action that we’re hooking, while the second argument is the function within my plugin file that will be ran.

  1. function _chikuncount_updatehits() {
  2.         global $wpdb;
  3.  
  4.         $table = $wpdb->prefix . 'chikuncounter';
  5.  
  6.         // We'll just update our hitcounter
  7.         $ip = $_SERVER['REMOTE_ADDR'];
  8.         $host = strtolower($_SERVER['HTTP_HOST']);
  9.         $browser = $_SERVER['HTTP_USER_AGENT'];
  10.         $page = $_SERVER['REQUEST_URI'];
  11.  
  12.         // Avoid apache dummy connections
  13.         if($host == '' || $ip == '::1')
  14.                 return;
  15.  
  16.         // Ignore favicon and robots.txt
  17.         if($page == '/favicon.ico' || $page == '/robots.txt')
  18.                 return;
  19.  
  20.         $sql = "INSERT INTO $table (ip, browser, page, host, time) VALUES ('$ip', '$browser', '$page', '$host', NOW());";
  21.  
  22.         $wpdb->query($sql);
  23. }

And there it is.  You may notice the similarities between this function and the functions in my Creating a Hitcounter post, and thats because I pretty much copied that ;-) .  The only differences are there for compatibility to WP.  Oh, and I also filtered out Apache dummy connections, favicon, and robots…Because ‘cmon, who really wants to count those as hits.

Outputting CSV Files

One of the major points of ChikunCount is that it can export CSV files with quite a few of your hit stats.  I haven’t gone through and added all of what I want yet, but more reports are coming for those of you who are using the plugin.

I had some problems deciding exactly how I wanted to action the CSV reports.  With WP development, you can’t (not that I’ve found yet, and TBH, I’m rather noobish with WP development) add an action to the admin page without having a menu link for it. Edit:  Actually I don’t know why this hadn’t occurred to me but you can use this exact type of function to make multiple “options” pages for your plugin.  Normally these actions would be displaying an options page or something similar, but we want to download a CSV file.  After trying a couple things out I finally settled on hooking the “admin_init” action, and sending WP through a passthrough function that examines the URL.

  1. // Actions we have for reporting
  2. // We want this priority 0 sense we're going to be playing with headers
  3. add_action('admin_init', '_chikuncount_report', 0);
  4.  
  5. function _chikuncount_report() {
  6.         if(isset($_GET['chikuncount'])) {
  7.                 // We want a report
  8.                 $chikunaction = $_GET['chikuncount'];
  9.  
  10.                 switch($chikunaction) {
  11.                         // Total hits section
  12.                         case "totalhits-byday":
  13.                                 _chikuncount_report_totalhits('day');
  14.                                 break;
  15.                         case "totalhits-byweek":
  16.                                 _chikuncount_report_totalhits('week');
  17.                                 break;
  18.                         case "totalhits-bymonth":
  19.                                 _chikuncount_report_totalhits('month');
  20.                                 break;
  21.                         case "totalhits-byyear":
  22.                                 _chikuncount_report_totalhits('year');
  23.                                 break;
  24.  
  25.                         // Hits by blog
  26.                         case "hitsbyblog":
  27.                                 _chikuncount_report_byblog();
  28.                                 break;
  29.  
  30.                         // Unique visitors
  31.                         case "unique-byday":
  32.                                 _chikuncount_report_unique('day');
  33.                                 break;
  34.                         case "unique-byweek":
  35.                                 _chikuncount_report_unique('week');
  36.                                 break;
  37.                         case "unique-bymonth":
  38.                                 _chikuncount_report_unique('month');
  39.                                 break;
  40.                         case "unique-byyear":
  41.                                 _chikuncount_report_unique('year');
  42.                                 break;
  43.  
  44.                         // Flash section
  45.                         case "sevendays-ofc":
  46.                                 _chikuncount_report_totalhits_ofc();
  47.                                 break;
  48.                         case "browsers-ofc":
  49.                                 _chikuncount_report_browsers_ofc();
  50.                                 break;
  51.                 }
  52.         }
  53. }

What we’re doing here is hooking the admin_init function, and checking to see if the GET variable “chikunaction” is set.  (In case you were wondering, the third argument for this add_action function is the priority that the function will be called, lower = sooner.)  If chikunaction is set, then we take it’s value and send it to the appropriate function to generate the report.  If chikunaction is NOT set, or set with something other than a valid function, we just pass through and let execution continue normally.

In the case that we have a valid report to run, here’s one of the functions that makes the magic happen:

  1. function _chikuncount_report_totalhits($type) {
  2.         global $wpdb;
  3.  
  4.         $table = $wpdb->prefix . 'chikuncounter';
  5.  
  6.         // Build our sql based on what our type is
  7.         switch($type) {
  8.                 case 'day':
  9.                         $sql = "SELECT COUNT(ip), DATE(time) FROM $table GROUP BY DATE(time);";
  10.                         $file = 'total-hits-by-day';
  11.                         break;
  12.                 case 'week':
  13.                         $sql = "SELECT COUNT(ip), WEEK(time) FROM $table GROUP BY WEEK(time);";
  14.                         $file = 'total-hits-by-week';
  15.                         break;
  16.                 case 'month':
  17.                         $sql = "SELECT COUNT(ip), MONTH(time) FROM $table GROUP BY MONTH(time);";
  18.                         $file = 'total-hits-by-month';
  19.                         break;
  20.                 case 'year':
  21.                         $sql = "SELECT COUNT(ip), YEAR(time) FROM $table GROUP BY YEAR(time);";
  22.                         $file = 'total-hits-by-year';
  23.                         break;
  24.         }
  25.  
  26.         $output = '';
  27.  
  28.         $result = $wpdb->get_results($sql, ARRAY_N);
  29.  
  30.         foreach($result as $row) {
  31.                 $output .= $row[1] . ',' . $row[0] . "\r\n";
  32.         }
  33.  
  34.         header("Pragma: public");
  35.         header("Expires: 0");
  36.         header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  37.         header("Cache-Control: private",false);
  38.         header("Content-Type: text/csv");
  39.         header("Content-Disposition: attachment; filename=\"$file.csv\";");
  40.         header("Content-Transfer-Encoding: binary");
  41.         header("Content-Length: ".strlen($output));
  42.  
  43.         echo $output;
  44.  
  45.         die();
  46. }

Here we’re building our SQL statement based on the type of report we’re running (based on days, weeks, etc) so that we have the correct data.  Then taking the SQL results and dumping them into a string that will become our CSV file (format: date,hits\r\ndate,hits).  Next we dump some header information to the browser so it knows that we’re sending across a text/csv and not to cache anything.  Then we simply echo the CSV string so that the browser picks it up, and stop WP execution by calling die().

Obviously theres a lot more that goes into the plugin than that, and I may elaborate on the rest in some later blog.  However, for now it appears we’re out of time.  If you want to get into some of the more inner workings of ChikunCount, feel free to download it and rip apart the source code all you want :-) .

Till next time.

Creating a Hitcounter with MySQL and PHP

While there are a lot of free ways to get traffic reports on your site, I came up with this idea for a hitcounter for a site that I had written by hand just so I could pull my own data off it.  It’s nice to have something you can just drop into a site, but doesn’t necessarily give you what you want.  Also doing it yourself gives you control over how long you keep the data.  I use this hitcounter with multiple PHP applications (Joomla, PHPBB3, and some more), but also with websites that I’ve handcrafted.  I haven’t put it into wordpress yet, but here we go.

MySQL Setup

The first thing we need to know is what data do we want to collect?  Here’s what I came up with:

  • IP Address of the client (of course)
  • Browser
  • Page (The page the client is hitting e.g. “/index.php” or “/index.php?action=news”)
  • Host (The host the client is viewing e.g. “www.nernonline.net” or “forum.nernonline.net”)
  • Time

All of this is pretty simple to grab from a PHP script, but we’ll go into that a bit later.  For now, we need to setup the MySQL table.  I use a separate database for my hitcounter table, not linked with any of the other databases that are running on the same box.  Copy this code into “hitcounter.sql” and run it by typing “mysql < hitcounter.sql”.  This is of course assuming that you are on your box, have the mysql command line interface installed, and have write permissions.

  1. – Create the our blank database
  2. CREATE DATABSE `hitcounter`;
  3.  
  4. USE hitcounter;
  5. – Hitcounter table
  6. CREATE TABLE `hits` (
  7.         `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  8.         `ip` TEXT NOT NULL,
  9.         `browser` TEXT NOT NULL,
  10.         `page` TEXT NOT NULL,
  11.         `host` TEXT NOT NULL,
  12.         `TIME` DATETIME NOT NULL);

The next step is to create a user that can write to this database.  All of your databases should have different users, with different passwords…Just for a bit of security : ).  You can run this either at the MySQL prompt or by putting it in a file and doing the same technique as above.

  1. GRANT ALL ON `hitcounter`.* TO 'username'@'localhost' IDENTIFIED BY 'password';

Obviously change “username” and “password” to something thats a little more meaningful and secure : ).  Also, the @’localhost’ portion tells mysql that this user can NOT login unless the connection is coming from the box itself.  If your Apache / IIS is on the same box as your database, leave localhost there.  If you need this user to be able to login from anywhere, change that to @’%', though I don’t know why you would for a hitcounter ; ).

PHP Stuffs

Now that our database is setup, we need to get something to go into it.  I use pear’s MDB2 for this.  If you don’t have it installed, on a Linux system you can type “pear install MDB2″ to get it, but I don’t know what to do if your running a Windows server, sorry.  I have a seperate .php file that I include with my globals.php file, so it is loaded every time a page is requested.

I have two functions inside my php file, one for updating the hits to a website, and one for getting the number of hits for a particular website.  Here’s the update function:

  1. function updatehits() {
  2.         // Include Pear's MDB2
  3.         require_once("MDB2.php");
  4.  
  5.         // Build our connection "string" (array)
  6.         $dsn = array(
  7.                 'phptype' => 'mysqli',
  8.                 'username' => 'username',
  9.                 'password' => 'password',
  10.                 'hostspec' => 'localhost',
  11.                 'database' => 'hitcounter' );
  12.  
  13.         // Create a singleton DB Connection
  14.         $hitdb = MDB2::singleton($dsn);
  15.  
  16.         // Get all the information we care about
  17.         $ip = $_SERVER['REMOTE_ADDR'];
  18.         $host = strtolower($_SERVER['HTTP_HOST']);
  19.         $browser = $_SERVER['HTTP_USER_AGENT'];
  20.         $page = $_SERVER['REQUEST_URI'];
  21.  
  22.         if($host == "" || $ip == "::1") {
  23.                 return;
  24.         }
  25.  
  26.         $sql = "INSERT INTO hits (ip, browser, page, host, time) VALUES ('$ip', '$browser', '$page', '$host', NOW());";
  27.  
  28.         $hitdb->query($sql);
  29.  
  30.         if(PEAR::iserror($hitdb)) {
  31.                 die($hitdb->getMessage());
  32.         }
  33. }

Of course, your going to want to replace username and password again.  This function pulls all the information that we care about out of PHP and dumps it nicely into our database.  The next function gets the amount of hits for the current website that your at.  I use this to put the “This site has X hits since Whenever” stuff at the bottom of the page or wherever.

  1. function gethits() {
  2.  
  3.         require_once("MDB2.php");
  4.  
  5.         // Build our connection "string" (array)
  6.         $dsn = array(
  7.                 'phptype' => 'mysqli',
  8.                 'username' => 'username',
  9.                 'password' => 'password',
  10.                 'hostspec' => 'localhost',
  11.                 'database' => 'hitcounter' );
  12.  
  13.         $hitdb = MDB2::singleton($dsn);
  14.  
  15.         // SQL query to get the hits from the website currently being viewed
  16.         $sql = "SELECT COUNT(*) FROM hits WHERE host = '" . strtolower($_SERVER['HTTP_HOST']) . "';";
  17.  
  18.         $result = $hitdb->query($sql);
  19.  
  20.         if($result->numRows() < 1) {                 return ('0');         }         $row = $result->fetchRow();
  21.  
  22.         return ($row[0]);
  23. }

Now then, after you have those two functions in your file, all you have to do is include that file with something that gets included no matter what page is hit on your website.  Usually index.php or something similar.

  1. require_once('updatehits.php');
  2. updatehits();

Anywhere you want to display how many hits that webpage has, just call the “gethits();” function, and it returns an unformatted number, which you can format any way you like using php’s number_format function.

Helpful MySQL Scripts

I use these MySQL snippets all the time, to check how many hits my webpages have gotten, unique IPs, etc.

Check how many hits your websites get by host:

  1. SELECT `host`, COUNT(*)
  2. FROM `hits`
  3. GROUP BY `host`;

Total hits by day:

  1. SELECT DATE(TIME) "Day", count(*) "Hits"
  2. FROM `hits`
  3. GROUP BY DATE(TIME);

Total hits by week:

  1. SELECT WEEK(TIME) "Week", count(*) "Hits"
  2. FROM `hits`
  3. GROUP BY WEEK(TIME);

To get unique IPs per month:

  1. SELECT COUNT(ip), MONTH(TIME)
  2. FROM (
  3. SELECT ip, TIME
  4. FROM hits
  5. GROUP BY ip ) as foo
  6. GROUP BY MONTH(TIME);

And that should do it!

C# Event Handling

Well, this is my first post on this blog, so I figured I would go with something a little on the basic side, but something I had a lot of trouble dealing with when I started out programming.  Creating and using events in C#.

So, lets say that your  trying to use a progress bar on a form to report the status of some long running task taking place in a separate class file you have.  The first thing you need to do is create a method delegate in your class file. Then, we’ll need to create the event itself, also in the class file.

public delegate void progressUpdateDel(int min, int max, int current);
public event progressUpdateDel progressUpdate;

The delegate defines how the method that receives the event has to be formed, and what variables will be coming across.  The event declaration simply says that there is an event, and it will be in the progressUpdateDel format.

The next step is going to be to subscribe your form to the class.  You’ll be doing this the same way that you would subscribe to a button’s onClick event.

WorkerClass myClass = new WorkerClass();
myClass.progressUpdate += new myClass.progressUpdateDel(myClass_progressUpdate);

In Visual Studio, after you type out myClass.progressUpdate you can hit tab twice to have everything including the += auto-inserted, and it will also write out a corresponding method for you.  Kinda handy : -).  Here’s what the function should look like in your form class.

private void myClass_progressUpdate(int min, int max, int current)
{
     this.progressBar1.min = min;
     this.progressBar1.max = max;
     this.progressBar1.value = current;
}

And finally all thats left is actually calling the event from your worker class. When the event is called, it automagically activates any methods that have subscribed to it. Pretty nifty, and can be used for, well, just about anything.

...
if(this.progressUpdate != null)
     progressUpdate(0, myMax, myCurrent);
...

Thats it!  Nothin more to it than that.  The if line when your calling progressUpdate is very important.  Calling the event will cause an exception if there are no other classes subscribed to its event, so don’t forget that line!