X Autoload: Unit test abstractions, InjectedAPIs

This is part of a series about
X Autoload

Virtualize the filesystem.

In symfony, a class finder implementation needs to do a file_exists() whenever it finds a candidate filepath. If that passes, it will return the candidate filepath. If not, it will try the same with the next candidate, and eventually return NULL.

This has two problems:

  • We do not know anything about candidates that were rejected due to a file_exists call that doesn't pass.
  • We need nasty tricks to imitate file_exists() for testing.

InjectedAPIs ...

To solve these problems, we make use of a pattern that is used in a lot of dqxtech code, typically going under the name "Injected API".

An "Injected API" is an object passed to a function as a parameter, with no other purpose than to let this function interact with the outside world.

Instead of sending a return value, the function will call one or more methods on the InjectedAPI object. After the function call, the InjectedAPI object is asked for the information it was able to collect, and is then released into nirvana.

This pattern can have several advantages, of which I shall speak in another blog post.

... applied to xautoload

In our $finder->findFile() methods, the InjectedAPI is sent as the first argument. Instead of doing any explicit file_exists(), the findFile() simply sends the candidate filepath to $api->suggestFile()

function findFile($api, $class) {  
  // Let $api do the file_exists() check.  
  if ($api->suggestFile("$module_dir/lib/Foo.php")) {  
    return TRUE;  

We can now easily replace that $api object by a mocked object for unit testing, which can record any suggested candidates, and simulate file_exists() on a virtual filesystem.