08 Jul

first PHP4/5 difference I've noticed

I just spent a half hour narrowing down a bug in an application I’d fut some polishing touches on in PHP5. When it was moved onto the production server earlier, it came up with blank pages. I wasn’t on hand to check it out, so couldn’t help over the phone. I had to wait until I could get to a computer to debug it.

The end problem was in a line similar to the following:

$n=$db->query('select id from some_table')->numRows();

The above worked in every server I had available – except the one it was supposed to go on.

To get that code working in PHP4, you must break the object results down:

$n=$db->query('select id from some_table');
$n=$n->numRows();

It’s an annoyance – but at least it’s not a large thing to fix, and the lines that need changing can easily be found by grepping for numRows().

06 Jul

IE bugs – dynamically creating form elements

I have a few scripts which have forms completely generated by JavaScript. In most browsers, this is fine, but IE, as usual, is “special”.

Look at this code:

var i=document.createElement('input');
i.name='action';
i.value='submit this form';
i.type='submit';

You would expect the above to provide something similar to this HTML:

<input type="submit" name="action" value="submit this form" >

Instead, the final line overwrites the values we’ve set and changes it to:

<input type="submit" name="action" value="Submit Query" >

That is annoying!

To work around that, make sure to set the type first.

var i=document.createElement('input');
i.type='submit';
i.name='action';
i.value='submit this form';

Another thing is multipart/form-data forms. With most browsers, you can create a form with the following code:

var form=document.createElement('form');
form.enctype='multipart/form-data';
form.action='shoppingcart_xhr.php';
form.method='POST';
form.target='shoppingcart_iframe';

The above code will mostly work in IE, except when you actually need to use the multipart aspect of it (uploading a file, for example), when it will barf.

In this case, IE seems to insist on the following crappy code:

form=document.createElement('<form action="shoppingcart_xhr.php" method="POST" enctype="multipart/form-data" target="shoppingcart_iframe">');

This is very annoying, and not friendly to my cross-browser sensibilities. But… at least it’s just the occasional form that needs that crap.

05 Jul

Handling :before properly in IE6

IE6 and IE5 do not support CSS2’s ‘:before‘. This is a pity, as it allows some pretty cool stuff to be done.

I’m currently working on an InkJet cartridge supplier’s website. One of the elements that the designer came up with is panels that have an interesting border effect at the top:

The image above is a screenshot taken with Firefox. The code to produce that effect was:

#columncenter:before {
    content:url(/extras/i/panelcorner.png);
    height:18px;
    display:block;
    background:transparent url(/extras/i/panelcorner.png) right top no-repeat
}
#columncenter{
    margin:0 180px;
    border:1px solid #8c8c8c;
    background:url(/extras/i/paneltopbar.png) top repeat-x
}

Note that there are three images mentioned. The two corner images are provided with the :before construct (you can use the same method to do rounded corners), and the background gradient is provided by the main element.

In order for the background gradient to show, it is important that the :before pseudo-element be truly transparent, as defined in the CSS. Unfortunately, the IE7 script cannot do this, as it was written to use object elements, which are not transparent in IE.

Here’s an IE screenshot. This is using IE7 0.8.

Note the big blank rectangle where a gradient should be. This is caused by the object taking on the background colour of the main element, but not the background image.

I had a fix for IE7 0.7, but it was not integrated with the master code. That’s a pity…

Anyway… Here’s a fix for IE7 0.8

In the recalc function in ie7-standard-p.js, replace the following code:

        // insert the pseudo element
        var $html = PseudoElement[$url?"OBJECT":"ANON"].replace(/%1/, this.className);
        var $$cssText = $generated.cssText;
        if ($url) {
          var $pseudoElement = document.createElement($html);
          $target.insertAdjacentElement($$position, $pseudoElement);
          $pseudoElement.data = _contentPath;
          addTimer($pseudoElement, $$cssText, Quote.remove($url[1]));
        } else {
          $html = $html.replace(/%2/, $$cssText).replace(/%3/, $content);
          $target.insertAdjacentHTML($$position, $html);
        }

With this:

        // insert the pseudo element
        var isImage=$url && /^url\([^\)]*\.(gif|png|jpg).?\)$/.test($content);
        var $html = PseudoElement[$url&&!isImage?"OBJECT":"ANON"].replace(/%1/, this.className);
        var $$cssText = $generated.cssText;
        if ($url&&!isImage) {
          var $pseudoElement = document.createElement($html);
          $target.insertAdjacentElement($$position, $pseudoElement);
          $pseudoElement.data = _contentPath;
          addTimer($pseudoElement, $$cssText, Quote.remove($url[1]));
        } else {
          if(isImage)$content=$content.replace(/url\(/,'<img src="').replace(/\)/,'">');
          $html = $html.replace(/%2/, $$cssText).replace(/%3/, $content);
          $target.insertAdjacentHTML($$position, $html);
        }
 

That’s it. Now, IE6 works, and we can code again using nice, standard CSS.

05 Jul

Unobtrusive Javascript, and CSS Selectors

Check this out!

I found this through Donncha’s blog. Here’s the explanation.

Basically, it’s unobtrusive JavaScript, using CSS selectors to apply the script, instead of class names.

The method I use myself is class names. For instance, check out Tyrone Guthrie’s website. The menu in that uses JavaScript, but I despise overuse of JavaScript, and always write as if for plain HTML, applying the script as an afterthought.

If you look at the source of that page, you will see that there is a script “/j/jsloader.js” loaded at the head of the page, and the only indication of where to apply the script is done by giving the navigation’s wrapping element a class of “magicmenuleft“. The script searches for that, then applies certain functions to elements containing that class.

The method used by Ben Nolan is similar, but even less intrusive. We both use CSS selectors, but his is possibly more efficient, bandwidth-wise.

There is a supposed problem which is that CSS classes are “supposed to be” used just for style. I don’t really think that’s the case, though. If you step back and look at it objectively, the word “class” means “group of similar items”. In this case, a class could be either a group of items that look the same, or a group of items with similar behaviour. I always choose the latter explanation when applying unobtrusive javascript using the class name method, but Ben’s method works around that, just in case there are any pedantic scripters around that are too snobby to stoop to misusing a class name 😉

Speaking of unobtrusive javascript; run a search, and you’ll see that it’s exploding at the moment. A few months ago, it was a mere blip on the radar, but it seems to be pretty popular now.

05 Jul

php5 failing on imagecreatefromjpeg

I had a very strange problem yesterday. I upgraded my office server recently, as I intended to use GD as my graphical library, instead of ImageMagick, which I had been using up until that point. The major difference is that ImageMagick is an external program which must be re-called every time it is used, and GD could be compiled as a linked library, making it quicker to use (imho).

So anyway – I did the upgrade, choosing PHP5 over PHP4 (not that I use its new OOP methods, fantastic as I’m sure they are).

Not being sure how all this would go, I chose a very simple configure script:

./configure --with-apxs2=/www/bin/apxs --with-gd \
  --with-mysql=/usr/local --with-pear --with-zlib

This compiled perfectly, with absolutely no warnings.

As I have four different machines that I test on – my laptop, my home server, the office test server, and the production server, I didn’t notice up until yesterday that the compilation wasn’t quite as perfect as I thought.

I’ve recently converted webme cms to use Pear’s Image_Transform instead of ImageMagick to perform its image manipulation, and was told yesterday that it wasn’t working on the office server.

A quick look was puzzling – no errors, no crashes – the scripts would just run, until an image was manipulated, then it would stop.

It took a bit of digging to find the problem. In order to find it, I first had to correct a few warnings in the Pear modules I was using (the Pear administrators have a policy of not giving a shit about strict code compliance, which means that their code is full of deprecated syntax and commands). I eventually tracked it down to an innocuous line, which called imagecreatefromjpeg.

Placing a line “echo 'test';exit;” before the line echoed ‘test’, but placing it after the line didn’t, so that was definitely the line.

I was a little bit stumped. I tried recompiling with the following configure script:

./configure --with-apxs2=/www/bin/apxs --with-gd \
  --with-mysql=/usr/local --with-pear --with-zlib --with-jpeg

That didn’t work.

Then I read the imagecreatefromjpeg manual page for any thoughts other people might have had – the answer was to explicitly tell PHP what directory the libjpeg.so was located in:

./configure --with-apxs2=/www/bin/apxs --with-gd \
  --with-mysql=/usr/local --with-pear --with-zlib \
  --with-jpeg-dir=/usr/lib

Flying colours!

I’m a bit annoyed that it took me a few hours to figure that out. It would have been nice to at least get an error message that the library hadn’t been found…