XHP: Interfaces

There are two important interfaces in XHP, XHPRoot and XHPChild; you will need to use these when adding type annotations to functions.

XHPRoot

The XHPRoot interface is a implemented by all XHP objects; in practice, this means:

  • :x:element subclasses - these are classes that re-use (and combine) existing XHP classes
  • :x:primitive subclasses - these define basic elements, such as :x:frag, and all of the basic HTML elements
  • implementations of the XHPUnsafeRenderable interface described below

XHPChild

XHP presents a tree structure, and this interface defines what can be valid child nodes of the tree; it includes:

  • All implementations of XHPRoot
  • strings, integers, floats
  • arrays of any of the above

Despite strings, integers, floats, and arrays not being objects, both the typechecker and HHVM consider them to implement this interface — both for parameter/return types, and for instanceof checks.

Advanced Interfaces

While XHP's safe-by-default features are usually beneficial, occasionally they need to be bypassed; the most common cases are:

  • Needing to embed the output from another template system when migrating to XHP.
  • Needing to embed HTML from another source, for example, Markdown or BBCode renderers.

XHP usually gets in the way of this by:

  • Escaping all variables, including your HTML code.
  • Enforcing child relationships - and XHP objects can not be marked as allowing HTML string children.

The XHPUnsafeRenderable and XHPAlwaysValidChild interfaces allow bypassing these safety mechanisms.

XHPUnsafeRenderable

If you need to render raw HTML strings, wrap them in a class that implements this interface and provides a toHTMLString(): string method:

<?hh
/* YOU PROBABLY SHOULDN'T DO THIS
 *
 * Even with a scary (and accurate) name, it tends to be over-used.
 * See below for an alternative.
 */
class ExamplePotentialXSSSecurityHole implements XHPUnsafeRenderable {
  public function __construct(
    private string $html,
  ) {
  }

  public function toHTMLString(): string {
    return $this->html;
  }
}

echo (
  <div class="markdown">
    {new ExamplePotentialXSSSecurityHole(
      HHVM\UserDocumentation\XHP\Examples\md_render('Markdown goes here')
    )}
  </div>
)."\n";

We do not provide an implementation of this interface as a generic implementation tends to be overused — instead, consider making more specific implementations:

<?hh
class ExampleMarkdownXHPWrapper implements XHPUnsafeRenderable {
  private string $html;
  public function __construct(
    string $markdown_source,
  ) {
    $this->html = HHVM\UserDocumentation\XHP\Examples\md_render(
      $markdown_source
    );
  }

  public function toHTMLString(): string {
    return $this->html;
  }
}

echo (
  <div class="markdown">
    {new ExampleMarkdownXHPWrapper('Markdown goes here')}
  </div>
)."\n";

XHPAlwaysValidChild

XHP's child validation can be bypassed by implementing this interface. Most classes that implement this interface are also implementations of XHPUnsafeRenderable, as the most common need is when a child is produced by another rendering or template system.

This can also be implemented by XHP objects, but this usually indicates that a child class specification should be replaced with a category. This interface is intentionally breaking part of XHP's safety, so should be used as sparingly as possible.

Example

<?hh
final class XHPUnsafeExample implements XHPUnsafeRenderable {
  public function toHTMLString(): string {
    return '<script>'.$_GET['I_LOVE_XSS'].'</script>';
  }
}

$inputs = Map {
  '<div />' => <div />,
  '<x:frag />' => <x:frag />,
  '"foo"' => 'foo',
  '3' => 3,
  'true' => true,
  'null' => null,
  'new stdClass()' => new stdClass(),
  '[<li />, <li />, <li />]' => [<li />, <li />, <li />],
  'XHPUnsafeExample' => new XHPUnsafeExample(),
};

$max_label_len = max($inputs->mapWithKey(($k, $_) ==> strlen($k)));
print str_repeat(' ', $max_label_len + 1)." | XHPRoot | XHPChild\n";
print str_repeat('-', $max_label_len + 1)."-|---------|----------\n";

foreach ($inputs as $label => $input) {
  printf(
    " %{$max_label_len}s | %-7s | %s\n",
    $label,
    $input instanceof XHPRoot ? 'yes' : 'no',
    $input instanceof XHPChild ? 'yes' : 'no',
  );
}
Output
                          | XHPRoot | XHPChild
--------------------------|---------|----------
                  <div /> | yes     | yes
               <x:frag /> | yes     | yes
                    "foo" | no      | yes
                        3 | no      | yes
                     true | no      | no
                     null | no      | no
           new stdClass() | no      | no
 [<li />, <li />, <li />] | no      | yes
         XHPUnsafeExample | no      | yes