Archive for the ‘PHP’ Category

Custom order sorting in PHP

Sunday, May 2nd, 2010

Every once in a while you find your self in need of a generic sorting function that orders in a very specific, and non-machine understandable way. Like sorting items from ‘one’ to ‘four’. Instead of building a small subset of those kind of sort operations into the language PHP has usort family (short for user-defined sort). In the background this sort still works as a quicksort, but the developer is tasked with making a function that does the comparison between different elements of the array. It’s up to him to define whether a value is smaller, equal or bigger than another.

So, keeping the ‘one’ to ‘four’ example, I present you with a very simple custom sort function.

function customCompare($a, $b) {
  // We flip the array to get the positional indices 
  $order = array_flip(array('one', 'two', 'three', 'four'));
 
  // Sanity check, $a and $b must exist in the order array
  foreach(array($a, $b) as $arg) {
    if(!array_key_exists($arg, $order))
      trigger_error("`$arg' is not a sortable key");
  }
 
  // The positions in the order array dictate if $a is bigger
  // than $b, as such we can simply subtract the integers
  // from the flipper order array to get an integer value
  // which is smaller, equal or bigger than 0 depending on
  // $a and $b's relative position in the array.
  return $order[$a] - $order[$b];
}
 
// An example array to be sorted
$example = array(
  'four' => 4,
  'one' => 1,
  'three' => 3,
  'two' => 2);
 
// Do the actual sort
uksort($example, 'customCompare');
 
// Dump results
print_r($example);

The dumped array will look like this

Array
(
    [one] => 1
    [two] => 2
    [three] => 3
    [four] => 4
)

If you, like me, like closures and are running PHP 5.3, we can make the compare function a lot nicer and easier to maintain.

function createSorter($order) {
  $order = array_flip($order);
 
  return function($a, $b) use ($order) {
    // Sanity check, again
    foreach(array($a, $b) as $arg) {
      if(!array_key_exists($arg, $order))
        trigger_error("`$arg' is not a sortable key");
    }
 
    return $order[$a] - $order[$b];
  }
}
 
// Sort array $array in the 'one' to 'four' order.
uksort($array, createSorter(
  array('one', 'two', 'three', 'four')));

PHP and XML manipulation

Thursday, May 28th, 2009

Recently I’ve been fiddeling around with an old project of mine, and decided to extend it into a full-blown framework, just for kicks. Born from GDO, a ORM layer for PHP, it’s supposed to serve no real purpose but personal gratification.

Anyway, parallel with the framework I am building a website to showcase it. I also decided that in this specific case all the HTML documents should be built from either PHPs DOM or SimpleXML libraries, I personally dislike having HTML strewn across the project, so I’ve seperated structures like forms and tables into classes and the idea is that I can simply append them to the current page at a specific node and it’ll render the HTML when everything is added.

The problem

Here in lies the problem, PHPs XML libraries (at least the ones that I tried) require you to drag the top node to which you want to append whatever around in all helper classes and such, allow me to demonstrate.

<?php
$document = new DOMDocument();
 
$table = new DOMElement('table');
 
$row = new DOMElement('tr');
$row->appendChild(new DOMElement('td', 'test element'));
 
$table->appendChild($row);
$document->appendChild($table);

One would expect this all to work out fine and produce the following XML document;

<?xml version='1.0' encoding='utf-8' ?>
<table><tr><td>test element</td></tr></table>

In fact, it won’t. It won’t even run. PHP bails out during run time with the error “DOMException: No Modification Allowed Error“. This is because every DOMElement object not created with DOMDocument::createElement() is by default read-only, as you can read in the description of DOMDocument::__construct().

Besides begging the PHP developers to make every DOMElement not read-only on construction, the obvious solution would be to add the DOMElement in question to the DOMDocument and continue building from there, but that’s the problem. It seems counter-intuitive to pass the DOMElement or DOMDocument that you want to append your seperate structure to to every constructor of every XML producing class (or via normal methods of course).

I’ve taken the literal example of PHPs DOM classes, but I’ve tested SimpleXML as well, which provides even less flexibilty regarding node reuse and other fancy stuff one could want to do with XML documents.

Workarounds

There are several solutions to this problem, to name a few:

  • Create a singleton class that holds one instance of DOMDocument, and use a wrapper method somewhere to either get a valid DOMElement
  • Have all classes that produce XML nodes use a fake DOMDocument, and then use DOMDocument::importNode() at the return-point to import the nodes (very inefficient, especially when using a deep-copy).
  • Have the classes that produce XML nodes return nested arrays containing all name -> content relations of the nodes and run through them when it’s sensible to do so.

I’ll leave it as an exercise to the reader to pick out the best method. I haven’t found a solution yet, but I hope to contact the DOM XML module maintainer and discuss this with him/her.