Other Features: Autoloading

PHP has an autoloading mechanism, via spl_autoload_register() to where classes can be automatically loaded at runtime, reducing the need for a bunch of require and include statements at the beginning of a script.

The HHVM runtime enhances this autoloading by being able to autoload functions, constants, and traits, in addition to classes.

The interface for this enhanced autoloading is a Hack function called HH\autoload_set_paths(). This function takes two arguments: an array map of what to autoload and a string root path from where to start the autoloading.

Syntax

HH\autoload_set_paths(<autoload array map>, <root path for autoloading>);

The <autoload array map> is an array that has the following syntax:

$map = array(
    <'function' | 'class' | 'constant' | 'type'> => 
    array(<name of entity> => <path to file of entity>,...,),
    ['failure'] => <callback to call on autoload fail>
);

failure is an optional key. And the callback can be a closure or an actual function. The parameters must be string, string, representing the kind and name of what failed.

e.g.,

$map = array(
    'class' => array('A' => 'lib/A.php', 'B' => 'lib/B.php'),
    'function' => array('foo' => 'lib/MyFile.php')
    'failure' => function($kind, $name) { echo "Autoload fail: $kind, $name\n"; }
);

Examples

Success

Here is an example of using the enhanced autoloading and being successful.

<?hh

function al_autoload_fail(string $kind, string $name) {
  echo "Autoload Fail: $kind with name $name failed to load\n";
}

// The al_userid type alias was autloaded below.
function al_testme(al_userid $id): bool {
  return true;
}

function al_run(): void {
  // This should typecheck but does not
  // UNSAFE
  $map = array(
    'function' => array('al_foo' => 'A.inc.php', 'al_bar' => 'B.inc.php'),
    'class' => array('AL_A' => 'A.inc.php'),
    'constant' => array('AL_BAZ' => 'B.inc.php', 'AL_YAM' => 'B.inc.php'),
    'type' => array('al_userid' => 'A.inc.php'), // type alias
    'failure' => 'al_autoload_fail'
  );
  \HH\autoload_set_paths($map, __DIR__ . '/');
  var_dump(al_foo());
  var_dump(AL_BAZ);
  var_dump(new AL_A());
  var_dump(al_testme(4));
}

al_run();
Output
string(100) "/data/users/joelm/user-documentation/guides/hack/47-other-features/04-autoloading-examples/A.inc.php"
bool(true)
string(100) "/data/users/joelm/user-documentation/guides/hack/47-other-features/04-autoloading-examples/B.inc.php"
int(1)
object(AL_A)#1 (0) {
}
bool(true)

Failure

Here is an example of using the enhanced autoloading and failing.

<?hh

function alf_autoload_fail(string $kind, string $name) {
  echo "Autoload Fail: $kind with name $name failed to load\n";
}

// The al_userid type alias was autloaded below.
function alf_testme(al_userid $id): bool {
  return true;
}

function alf_run(): void {
  // This should typecheck but does not
  // UNSAFE
  $map = array(
    'function' => array('al_blahblah' => 'A.inc.php', 'al_bar' => 'B.inc.php'),
    'class' => array('AL_A' => 'A.inc.php'),
    'constant' => array('AL_BAZ' => 'B.inc.php', 'AL_YAM' => 'B.inc.php'),
    'type' => array('al_userid' => 'A.inc.php'), // type alias
    'failure' => function($kind, $name) {echo "Autoload fail: $kind, $name\n";}
  );
  \HH\autoload_set_paths($map, __DIR__ . '/');
  var_dump(al_blahblah());
}

alf_run();
Output
string(100) "/data/users/joelm/user-documentation/guides/hack/47-other-features/04-autoloading-examples/A.inc.php"
Autoload fail: function, al_blahblah

Fatal error: Call to undefined function al_blahblah() in /data/users/joelm/user-documentation/guides/hack/47-other-features/04-autoloading-examples/failure.php on line 23

We tried to use the autoloaded function so we got both the message that the autoload failed and a fatal error.

Caveats

  • For functions, the name in the autoload map is lowercase sensitive. This may be a bug, but it is that way right now. So if you have a function called Foo that you are autoloading, the map needs to have foo.
  • If your failure callback returns ?bool , and you are autoloading a class, returning false will raise a fatal error as normal when trying to use the class (just like a function). If you return null, then it falls back to normal spl_autoload_register autoloading.
  • The failure callback should be used primarily as a logging mechanism.