Contact - Twitter Icon Twitter - RSS Feed Icon RSS

TomNomNom.com

The web stuff of Tom Hudson.

Bio

Friends

Files

Tweets

Syntax highlighting with PHP's tokenizer Wed Oct 7 21:24:01 2009

While I was changing my design from light to dark, I hit a problem with my syntax highlighting: the colours were just too damn dark to see on a dark background. Because I only usually write about PHP, I just use highlight_string() for my syntax highlighting - rather than one of the more popular JavaScript syntax highlighters. To change the colours I can just call ini_set() to set highlight.keyword etc to whatever I want.

That's all fine and dandy, but one of the things I really wanted was to give 'users' the ability to switch to whatever style they want. That could get quite complicated (and slow) if I just used ini_set() to do it. That, and I'd rather it were possible without re-loading the page.

My solution is to hack together my own function for highlighting PHP, but instead of having it specify the colour of each section directly in the style attribute of the span tag (like highlight_string), have it specify a class that I can change the appearance of with regular old CSS.

And it looks a little like this:


<?php
function highlightPhp($source){
  $tokens = token_get_all($source);
  $output = '';
  foreach ($tokens as $token){
    if (is_string($token)){
      $output .= htmlEntities($token);
    } else if (is_array($token)) {
      list($id$text) = $token;
      $name = str_replace('_''-'strToLower(token_name($id)));
      $text = htmlEntities($text);
      $text = str_replace(
        array(" ",      "\n"),
        array("&nbsp;""<br/>"),
        $text
      );
      if ($id != T_WHITESPACE){
        $text = "<span class=\"phps-{$name}\">{$text}</span>";
      }
      $output .= $text;
    }
  }
  return $output;
}

Note: As of about an hour after I posted this, the code above was highlighted with itself!

With CSS that looks something like this:


<style type="text/css">
  /* Don't use this CSS. Your code will look HORRIBLE ;-) */
  .phps-t-open-tag {color: #DD3333;}
  .phps-t-variable {color: #33DD33;}
  .phps-t-if,
    .phps-t-while,
    .phps-t-exit {color: #3333DD;}
  .phps-t-string,
    .phps-t-echo {color: #33DDDD}
  .phps-t-isset {color: #DD33DD}
  .phps-t-lnumber {color: #DDDD33}
  .phps-t-encapsed-and-whitespace,
    .phps-t-constant-encapsed-string {color: #DDDDDD}
</style>


It's a little primitive at the moment, and I haven't bench-marked it, but it seems to do the job.

And it does have one other advantage over highlight_string(): it provides much more granular control over the colours. highlight_string() only provides six possible colours (highlight.bg, highlight.comment, highlight.default, highlight.html, highlight.keyword, highlight.string), rather than all of these.

Hopefully I'll be implementing it here soon. I'll be sure to provide some benchmarks compared to highlight_string().

***EDIT***
I have now implemented this.

I am not a designer Tue Oct 6 23:50:02 2009

I've changed my design a little in the past couple of days - to a light, minimal one at first; and after a suggestion from @scawp, to a dark one.

I knew it already, but it's really made me realise that I am not a designer. I had a really hard time just picking the colours.

Is this one a keeper? Hit the contact link at the top of the page and let me know. (And remind me that I need to finish my comments system).

WinCacheGrind timing fix Sat Sep 26 17:41:27 2009

There is a reasonably well known bug with WinCacheGrind; when using cachegrind.out files produced by XDebug the times are out by a factor of ten. While it's pretty easy to mentally multiply the times by ten, times below 0.1ms aren't displayed; meaning you don't actually see the times for anything that took less than 1ms to execute.

I originally intended to modify the source code for WinCacheGrind and host a fixed version, but it would seem there is a file missing from the source code - and I don't know enough about Delphi to hack around it.

My alternate fix is a little PHP shell script that I've put in /usr/bin/wcgfix on my development box. It just multiplies all the times by ten.


#!/usr/bin/php
<?php
if (!isSet($argv[1])) die ("Usage {$argv[0]} <filename>\n");
$file = $argv[1];
$fh = fopen($file'r');
if (!$fhdie("Could not open file");

while (!feof($fh)){
  $l = fgets($fh);
  if (is_numeric($l[0])){
    $p = explode(' '$l);
    $p[1] = $p[1] * 10;
    $p[] = "\n";
    $l = implode(' '$p);
  }
  echo $l;
}
fclose($fh);


The timing lines in cachegrind.out files are in the format <line number> <execution time> <unknown>. None of the other lines in the file start with a number, which makes them pretty damn easy to dig out.

Typical usage would be something like:


# wcgfix cachegrind.out.borked > cachegrind.out.fixed


I admit, it's still a little bit of a pain. But until I manage to get this damn Delphi thing sorted, it will have to do!

Dynamic callbacks for PHP's usort Thu Sep 24 16:41:28 2009

I recently wrote a blog post on a couple of PHP5.3's new features. As you may have noticed, I was really clutching at straws with my example use-cases. To make up for that, I have come up with a more realistic example of when you might want to use closures.

I seem to end up using multi-dimensional arrays quite often. Whatever the reason for that, sooner or later I need to sort them. In the past, I have just defined a callback function for use with usort(), but that has a lot of drawbacks.

If I am to successfully explain what the hell I'm on about, we will be needing some test data... Oh look! Here it comes now! Five different types of fruit in a multi-dimensional array.


<?php
$fruit = array(
  array('number' => 1'name' => 'apple',  'color' => 'red'),
  array('number' => 2'name' => 'orange''color' => 'orange'),
  array('number' => 3'name' => 'pear',   'color' => 'green'),
  array('number' => 4'name' => 'grape',  'color' => 'red'),
  array('number' => 5'name' => 'peach',  'color' => 'peach')
);


Come to think of it: we will be needing a nice way to tell people what fruit we have and in what order. Here's a dead simple function for doing just that:


<?php
function printItems($items){
  foreach ($items as $item){
    echo implode(', '$item) . "\n";
  }
}


Let's give it a whirl!


<?php
printItems($fruit);
//Outputs:
//1, apple, red
//2, orange, orange
//3, pear, green
//4, grape, red
//5, peach, peach


Of course, when we print our original array it comes out in the order we defined it.

The old way


I'm a bit picky. I don't really want to tell people about my fruit in the order I thought of them. I'd much rather they were in alphabetical order. PHP doesn't have a native way to do that, so we need to define a sorting function and use usort() to do it.


<?php
function sortByName($a$b){
  return strCmp($a['name'], $b['name']);
}


The sorting function is used as a callback by usort(). It is given two elements from the array at a time; returns zero if they are the same, less than zero if the first one should come before the second, and more than zero if the second one should come before the first. Lucky for us, PHP's strCmp will do such a thing for us if we just want a string comparison; we just have to tell it which key we want to use.


<?php
usort($fruit'sortByName');
printItems($fruit);
//Outputs:
//1, apple, red
//4, grape, red
//2, orange, orange
//5, peach, peach
//3, pear, green


There is a problem with this however. If I wanted to then sort my fruit alphabetically by colour instead of by name, I would have to define a sortByColor() function. If I wanted to sort them by number, I would have to define a sortByNumber() function; and so on and so fifth.

What I need is a function that I can tell what I want to sort by. Before PHP5.3's introduction of closures that would have been possible, but tricky - maybe even ugly. PHP5.3 makes it trivial - maybe even elegant.

The easy way


Behold, the sortBy() function!


<?php
function sortBy(&$items$key){
  if (is_array($items)){
    return usort($itemsfunction($a$buse ($key){
      return strCmp($a[$key], $b[$key]);
    });
  }
  return false;
}


The input array $items is taken by reference and sorted with usort(). The callback function is created on the fly with a closure, useing the $key provided.

Let's try it out!


<?php
sortBy($fruit'name');
printItems($fruit);
//Outputs:
//1, apple, red
//4, grape, red
//2, orange, orange
//5, peach, peach
//3, pear, green

sortBy($fruit'color');
printItems($fruit);
//Outputs:
//3, pear, green
//2, orange, orange
//5, peach, peach
//4, grape, red
//1, apple, red


It's alive! IT'S ALIIIVE! Well, it works anyway. There's still a little something missing though.

What if I want to sort my fruit in descending order instead of ascending? That's actually a pretty easy change. All I need to do is add an option to invert the output of the strCmp() function.


<?php
function sortBy(&$items$key$descending = false){
  if (is_array($items)){
    return usort($itemsfunction($a$buse ($key$descending){
      $cmp = strCmp($a[$key], $b[$key]);
      return $descending? -$cmp : $cmp;
    });
  }
  return false;
}

sortBy($fruit'number'true);
printItems($fruit);
//Outputs:
//3, pear, green
//5, peach, peach
//2, orange, orange
//4, grape, red
//1, apple, red


Magic! I can now sort by any key I want, in any direction I want. I've never been so happy.

Not quite...


Those of you have been paying attention may have noticed the fatal flaw in my use of strCmp(); I know - I'm one of the people who have noticed. If you want to sort by a numeric value, strCmp() would tell you that 20 comes before 3, and that 70 comes after 400 etc, because it does a string comparison.

This is a problem I intend to address in a blog post coming soon. Stay tuned!

Yes, No, Cancel Sun Aug 23 15:06:10 2009

I made one of the oldest mistakes in the book today; assuming that there is a big book of mistakes somewhere. When using Adobe(R) Photoshop(R) software I tried to merge a layer down with ctrl+E, and hit ctrl+W instead. I was presented with this dialog:



The problem is that my first thought was "NO! NO NO NO! I don't want to do that!". So I clicked 'No'. Duuuuuuh.
It wasn't anything important I was working on, so I just left it and went to get a sandwich.

While what I did was a stupid mistake, it did help me realise that even the parts of interfaces that we take for granted as 'fine' have room for improvement. It also made me realise how tasty peanut butter and Rocky Caramel sandwiches are. And finally, from following the link to Fox's Biscuits, I was reminded how annoying Flash-based websites are when you want to link to a specific part of them. ANYWAY...

I'm sure I'm not the first to make such a suggestion, but I think something like this would cause less headaches:



Would that be so hard? I know it's lazy to not want to read the whole message, but laziness is in our nature.

For the record, I'm not picking on Adobe(R) Photoshop(R) software specifically. Many, if not most, applications have the same problem. It just so happened that I was using Adobe(R) Photoshop(R) software at the time.