Parsing the attributes of XML elements

November 5th, 2007

PHP 5.0 contains some excellent functions for parsing XML into arrays and objects, but that isn’t much good to you if you have to used PHP 4.0.

That said, it is relatively straightforward to extract the data from between XML tags in PHP 4.0, but the particular problem I came across was having to extract attribute values from the tags themselves. This arose in relation to exchange rate data published by the ECB, which you can view here:

http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

As you can see, the data we really want (the exchange rate and currency name) is an attribute value rather than raw data between the tags.

Anyway, to get at this data you need to look at the $attrs associative array that is passed to whatever ’start element’ function you are using. This will work (CUBE is the tagname we want in the ECB feed):

function startElement($parser, $name, $attrs) {
global $insidecube, $tag;

if ($insidecube) {
$tag = $name;
} elseif ($name == “CUBE”) {
foreach ( $attrs as $name => $value ) {
echo $name . ” ” . $value . “\n”;
}
$insidecube = true;
}

}

This just prints the attribute keys and values on your screen, but you get the idea.

What form am I?

October 19th, 2007

Its nice to have a single piece of Javascript that you can use on all the forms you have in an application, but it can be difficult between browsers to get your Javascript to recognise what form it has been called from.

Here’s a simple solution.

Give your form a numeric ID. That way, you can pass the ID to a function that can then do whatever it is you need to do with the form.

For example:

<FORM ID=100 NAME=registration_form ACTION=index.php METHOD=get>

<INPUT TYPE=text NAME=user_name>

<INPUT TYPE=button VALUE=submit_button onClick=’submitMe(100);’>

</FORM>

Your submitMe() function then looks like this:

function submitMe(formID) {

document.getElementById(formID).submit;

}

And if you have more than 1 form in a page, you can use different numeric IDs for each form: 100,101,102 etc

Finally, you can of course give your form a contextual name as well as an numeric ID (eg. “registration_form”) so that you can also access the form components and values through the DOM, ie:

var userName = document.registration_form.user_name.value;

Sweet.

Refreshing an image when filename is the same

September 19th, 2007

Here’s a nifty little trick that might get you out of a bind:

Say you have a web app in which the user can change the images by uploading new image files. Say then that the application requires that the image filename remain the same. See the problem?

The image may have changed but your browser is still looking at the same filename, so it will display its cached image. Of course, you could try an edit your HTML to that caching doesn’t occur, but we like caching, so here’s a better solution.

An image file, like a script file, can accept an argument when requested with HTTP. For instance, the image below is requested so:

<IMG src=/blog/wp-content/pottery-main.thumbnail.jpg?foo>

The cool thing about this is that you attach random arguments to your image calls, either in HTML or in Javascript, which will cause the browser to reload that image every time it is called. Hence, when your user loads a new image, and this image has the same file name as the old image, this method will allow the user to view the new image without having to do a manual refresh.

In Javascript, you can create a random argument using time and date functions. e.g.

my_image.src=”images/pottery.jpg?” + (new Date()).getTime();

or

image_td.innerHTML = “<IMG src=images/pottery.jpg?” + (new Date()).getTime() + “</IMG>”;

Managing images with GD

September 12th, 2007

One of the biggest problems I find with Content Management Systems is that while users are capable of managing web site text, the results are a little less predictable when it comes to managing images.

There are 2 primary problems:

Firstly, a lot of users aren’t comfortable with scaling images to fit into the spaces designed for them, and secondly, if you allow for this and use the IMG tag to scale the height and width of the image, you get fuzzy distorted images and large files that take an age to load in the browser.

This is why programatically manipulating uploaded images is such a good idea.

There are 2 tools you can use to manipulate images in PHP: GD or ImageMagick

GD is more commonly found in PHP compiles, and ImageMagick can be difficult to install correctly, so lets stick with GD for this discussion.

The problem I faced was this:

I wanted users to be able to upload long images, tall images and square images, but I wanted these to fit into a space 214px x 214px on a website page without having to use the IMG height and width tags.

This meant a 2 step process for the long and tall images: first, resize them so that they were with 214px tall or wide, and then crop them so that they were both 214px tall and 214px wide. I also wanted to make sure I was cropping down to the middle portion of the image, so that I was getting what the user wanted to appear and not the top, bottom or side of the image. The task for square images was a little simpler, in that I only had to resize the image (no cropping).

The PHP function I created is produced here. I’ve added inline comments to explain.

<?php

#Arguments = source image file, altered image file, required dimesions of altered image

function resize_image($src_image,$dst_image,$xsize,$ysize)
{

list($w,$h) = getimagesize($src_image);

#Deals with images that are wider than they are tall

if ($w > $h) {

#First, resize the image so that 1 side is the required length
$new_height = $ysize;
$new_width = ($ysize / $h) * $w;

$x=$w;
$y=$h;

#Resized temp image

$temp_image = “images/” . date(”U”) . “.jpg”;

$image = imagecreatefromjpeg($src_image);
$crop = imagecreatetruecolor($new_width,$new_height);
imagecopyresized ($crop, $image, 0, 0, 0, 0, $new_width, $new_height, $w, $h);
imagejpeg($crop,$temp_image,100);

# Now use temp image to create final image

list($w,$h) = getimagesize($temp_image);

if ($h >= $ysize) $new_height = $ysize;
if ($h <= $ysize) $new_height = $h;
$new_width = $ysize;

#This bit sets the dimensions for the middle of the image
$x = ($w – $ysize) / 2;
$y = 0;

$image = imagecreatefromjpeg($temp_image);
$crop = imagecreatetruecolor($new_width,$new_height);
imagecopy ($crop, $image, 0, 0, $x, $y, $new_width, $new_height);

#As above but for images that are taller than they are wide

} elseif ($h > $w) {

$new_width = $xsize;
$new_height = ($xsize / $w) * $h;

$x=$w;
$y=$h;

$temp_image = “images/shops/” . date(”U”) . “.jpg”;

$image = imagecreatefromjpeg($src_image);
$crop = imagecreatetruecolor($new_width,$new_height);
imagecopyresized ($crop, $image, 0, 0, 0, 0, $new_width, $new_height, $w, $h);
imagejpeg($crop,$temp_image,100);

list($w,$h) = getimagesize($temp_image);

if ($w >= $xsize) $new_width = $xsize;
if ($w <= $xsize) $new_width = $w;
$new_height = $xsize;
$y = ($h – $xsize) / 2;
$x = 0;

$image = imagecreatefromjpeg($temp_image);
$crop = imagecreatetruecolor($new_width,$new_height);
imagecopy ($crop, $image, 0, 0, $x, $y, $new_width, $new_height);

#Square image; no need for cropping
} elseif ($h == $w) {

$new_width = $xsize;
$new_height = $ysize;

$x=$w;
$y=$h;

$image = imagecreatefromjpeg($src_image);
$crop = imagecreatetruecolor($new_width,$new_height);
imagecopyresized ($crop, $image, 0, 0, 0, 0, $new_width, $new_height, $w, $h);

}

#Save the new images and discard any temporary images
imagejpeg($crop,$dst_image,100);
imagedestroy($image);
}

?>

Here’s an example:

This is the original image resized with IMG tags:

pottery-main.jpg

And here is the programatically manipulated image without any IMG tags:

v1-53.jpg

Debugging Javascript with Firebug

August 23rd, 2007

Javascript interests me for 2 reasons:

Firstly, in involves a different mindset, in that its client focused, rather than server focused, as is the case with most scripting languages.

Secondly, because its client focused, it involves challenges in terms of browser variations that simply don’t exist with server-side scripting.

The upshot of this is that you have to code Javascript with extended terms of reference, or in other words, ensuring that something works is only half the battle; you also have to ensure that it works everywhere.

That’s why you really need a good debugger, and why the Firebug Add-on for Firefox is a must have for any developer with an interest in Javascript and/or AJAX.

There are a number of issues to deal with when debugging Javascript. Firstly, errors in your Javascript code occur in your browser, so (paradoxically) that can’t be displayed in your browser window like PHP errors, which the PHP parser on your server can create for you and send to you in the HTTP conversation. That’s why Javascript errors generally end up as an icon in your status bar, which, particularly in the case of IE, are as good as useless.

Then there are the errors that are errors in some browsers but not in others, which indicate a use of the DOM which is not necessarily incorrect, just not universally supported.

This is where Firebug comes into its own. Firebug basically lifts the bonnet on everything that is going on behind the scenes in your Firefox browser. For instance, if Firefox makes an XMLHttpRequest as part of some AJAX functionality, Firebug will show what the response was generated by that request.

Firebug will also show you a hierarchical representation for the entire DOM of any given page, and allow you to expose every property and attribute, including CSS attributes, of any DOM element simply by right-clicking on that element in your web page.

Furthermore, Firebug will let you analyse the performance of your page, by clocking the load time if every HTTP request, including images, css files, javascript files and whatever else you page calls.

All of this is achieved in an easily accessed and ergonomic interface which fits neatly into your window when active and into your status bar when inactive.

Of course, the question is how does this help with IE, in that Firebug isn’t available for IE and that errors can occur in the IE DOM that don’t show up in Firefox. An example of this would be where Firefox allows you to set a DOM event like ‘onClick’ with the setAttribute function whereas IE doesn’t (IE is probably more correct in this regard; Firefox is just being nice).

One answer to this question could be: “Why not get an IE Javascript debugger?”, but as with the answer to most IE development questions, you’re going to have to get our your credit card, which is a really big ask when you know that Firebug is Open Source and probably a far better debugger than anything you can buy for IE.

A better answer is that you can use Firebug/Firefox in parallel with IE. Basically, this involves testing pages in IE and Firefox at the same time. Where universal errors occur, Firebug will pick them up and allow you to fix them for both broswers, and where browser specific errors occur, you can at least examine what is going on via the Firebug interface, which, most of the time will point you to where your browser specific error is occuring.

Another point to bear in mind is that IE 7 is a lot closer to Firefox/Mozilla than any other IE browser when it comes to DOM and Javascript issues, so having Firebug in the bag is just about a no-brainer at this stage.

You can get Firebug (and read a bit more) from here:

http://www.getfirebug.com

phpBB Robotic Registrations

August 21st, 2007

Preventing robots from posting on a phpBB forum is easy enough if your prospective users are content to wait for an administrator to moderate the registration, but that isn’t going to suit everybody.

Requiring moderation on the part of the administrator is also a pain for the administrator, who has to deal with the registration emails and decide who is real person and who is not.

I came across this problem for http://www.planningmatters.ie/pmbb

When you first become aware of the problem, your impulse reaction is to head off to Google for a tried and tested solution, but the problem is that once a solution gains any currency, the robot authors figure it out and you’re back to square one again.

My first solution was to edit the email that is sent to users awaiting activation, asking them to forward that email back to me to confirm they were a real person. I also set up a cron job to run an SQL script to delete non-activated users from the database, deeming that any user who had not been activated 7 days after initial registration to be a robotic user.

This isn’t ideal in that it requires an extra step for users, but it does mean that real users get through, and that your database is kept in shape.

However, I was still getting floods of email from attempted robotic registrations, so I set about editing the registration script (includes/usercp_register.php).

This script processes registrations based on whether or not the user has agreed to the disclaimer on the primary registration page. I set an extra PHP $_GET variable as part of the disclaimer agreement, and amended the later part of the script to check for that variable before processing the registration. I also set the extra variable equal to date(”DG”) which means that the variable changes every hour. You can see this by examining the discliamer links here:

http://www.planningmatters.ie/pmbb/profile.php?mode=register

The fact that the extra variable is not part of the standard phpBB install will ward off a lot of dumber robots, and the fact that it changes will ward off some smarter ones too. However, there are still a lot of robots out there that are clever enough to detect the verification method, so I was still getting some SPAM registrations.

To clear off the final few robots I knew I was going to have to involve the intellect of real users, in that this is probably the only thing that robots can’t replicate. Hence, I decided to add a really simple, but real, question to the registration page. To do this I edited the following file under the default template:

templates/subSilver/profile_add_body.tpl

I inserted the following extra lines of HTML underneath the Visual Confirmation section:

<tr>
<td class=”row1″><span class=”gen”>What is the day today?</span><br /><span class=”gensmall”>We ask this question to prevent SPAM registrations. SPAM robots won’t know the answer.</span></td>
<td class=”row2″><input type=”text” class=”post” style=”width: 200px” name=”day_today” size=”6″ value=”" /></td>
</tr>

This adds the following question to the registration form:

“What is the day today?”

The answer to which 99.999% of real users on the forum will know.

To check the answer, I then added an extra line to the top of the main registration script:

includes/usercp_register.php

The line I added is:

//ANTI-SPAM DEVICE
if (isset($HTTP_POST_VARS['day_today']) and strtolower($HTTP_POST_VARS['day_today']) != strtolower(date(”l”))) die();

This basically ensures that the answer to the question is the same as the day produced by the date() function (case insensitive) and if its not, the script dies.

Previous to adding this, and even with the other changes, I was getting about 20 robot registrations per day. After adding this, it dropped to about 10 per day, so there was still some work to do.

Finally, Occams Razor came to the rescue. I found out that the robots trawl for the “profile.php” script and the “mode=register” URI, so I set about trying to change these.

They need to be changed in:

includes/page_header.php

includes/usercp_register.php

(remembering of course to rename the file profile.php too)

I changed mine to:

profile.php -> pmatters.php

mode=register -> mode=signupuser

Now, FINALLY, I have stopped getting robotic registrations!!