HHVM and Hack – Can We Expect Them to Replace PHP?

Victor Berchet
Share

Facebook started to work on HipHop for PHP in 2008. Their goal was to speed up the PHP execution speed and the first version of the project was composed of the tandem HPHPc/HPHPi. HPHPc is a PHP to C++ transpiler which was used to deploy the code to the production servers while HPHPi was an interpreter used during development and debug stages.

HPHPc did a good job on improving performance but it was not without issues: maintaining both HPHPc and HPHPi in sync proved to be cumbersome plus some differences still existed between the transpiled code vs the interpreted one. That’s why back in 2010 Facebook decided to go for another approach and created HHVM – a new virtual machine designed to replace the Zend Engine used by PHP. By the end of 2012 HHVM achieved performance parity with the former HPHPc and soon surpassed it.

HHVM is intended to achieve both parity with the Zend Engine features and best possible performances. Facebook claims a 3x to 10x speed boost and 1/2 memory footprint by switching from PHP+APC to HHVM. Of course this is really application dependent (10x being for the FB code base). This article will not focus on parity nor performances as plenty of resources are already available, check the HHVM blog or google for “hhvm benchmark”. To find out more about HipHop and HHVM in general, read the previous SitePoint articles about it.

Instead this article will focus on HACK which is an evolution of the PHP language designed to be safer, to enable better performance and to improve developer efficiency. Note that both HACK and PHP are equally supported by the HHVM. Despite the fact that HACK is in use at Facebook on all the production servers, only little info has leaked for now. In a nutshell, HACK is Facebook’s PHP6 – it proposes to fix most of what’s wrong with PHP today, while adding some new features like static typing along the way.

Hello HACK

Not all the tools and almost no documentation for HACK have been released as of today. However the latest available HHVM source code already supports HACK. You can install a Vagrant virtual machine to start experimenting with HACK and run the code snippets from this article:

# Vagrant should be installed on your machine
$ git clone https://github.com/vicb/hhvm-vagrant.git
$ cd hhvm-vagrant
$ vagrant up

You are now ready to write your first HACK program:

<?hh
require "/vagrant/www/xhp/php-lib/init.php";

$hello = "Hello HACK!";

echo <html>
    <head>
        <title>{$hello}!</title>
    </head>
    <body>
        <h1>{$hello}</h1>
    </body>
</html>;

This sample is available in your clone of the vagrant VM (located at www/hello/index.php). You can see the result by pointing your browser to http://localhost:8080/hello/.

A HACK application starts with the <?hh tag located at the very start of your source file – note that there is no closing tag in HACK. This sample is pretty self-explanatory, the only noteworthy point is that is uses XHP, an XML markup language which is also available as a stock PHP extension. More about XHP later in this article.

More interesting HACK goodness will be introduced the following sections.

Constructor argument promotion

Not only is HACK more efficient than stock PHP when it comes to execution speed but it also focuses on improving developer efficiency.

“Constructor argument promotion” is one of the features helping reducing boilerplate code. Indeed, a common pattern when creating a class in PHP is first to declare the properties and then assign all of them straight from constructor arguments. With HACK, adding the visibility as a constructor argument modifier is enough for both declaring the property and initializing it. It makes the code more concise:

<?hh
class PHPClass {
    public $pub;
    protected $pro;
    private $pri;

    public function __construct($pub, $pro, $pri) {
        $this->pub = $pub;
        $this->pro = $pro;
        $this->pri = $pri;
    }
}

// object(PHPClass)#6 (3) { ["pub"]=> string(3) "pub" ["pro":protected]=> string(3) "pro" ["pri":"PHPClass":private]=> string(3) "pri" }
var_dump(new PHPClass('pub', 'pro', 'pri'));

// The same class written in HACK is much more concise 
// thanks to constructor argument promotion.
class HHClass {
    public function __construct(public $pub, protected $pro, private $pri) {}
}

// object(HHClass)#6 (3) { ["pub"]=> string(3) "pub" ["pro":protected]=> string(3) "pro" ["pri":"HHClass":private]=> string(3) "pri" }
var_dump(new HHClass('pub', 'pro', 'pri'));

The sample code for this section is located at www/promotion/index.php and you can see its output by pointing your browser to http://localhost:8080/promotion/.

Note that facebook has proposed this feature for inclusion in stock PHP.

Collections

HACK adds support for collections. Some of the available classes will look familiar to PHP developers while there are also long-awaited additions.

The Vector and Map types are an augmented version of PHP arrays in the sense that they provide the same functionality but expose a nice object oriented interface which is missing in stock PHP:

<?hh
require "/vagrant/www/xhp/php-lib/init.php";

$v = Vector {"d", "c", "b"};
$v->add("a");
$v->reverse();
$v = $v->map(($_) ==> strtoupper($_))->filter(($_) ==> strcmp($_, "D") < 0);

$items = <ul />;
foreach ($v as $i) {
    $items->appendChild(<li>{$i}</li>);
}
echo <div>Vector:{$items}</div>; // A, B, C

$m = Map {"a" => 1, "b" => 2};
$m->add(Pair {"d", 4});

echo <div>Map:
    <ul>
        <li>contains "a": {$m->contains("a") ? "yes" : "no"}</li> // yes
        <li>contains "c": {$m->contains("c") ? "yes" : "no"}</li> // no
        <li>size: {$m->count()}</li> // 3
    </ul>
</div>;

In this sample you can also notice the introduction of “lambdas”. They are a shorthand version of closures, ($args) ==> $result being the equivalent to function($args) { return $result; }.

The sample code for this section is located at www/collections/index.php and you can see its output by pointing your browser to http://localhost:8080/collections/.

HACK also provides a Set type – a collection of unique elements, a “Frozen” immutable variant for each of the above described types (to allow for further speed optimizations) a bunch of Iterators and many useful methods. You can learn more by checking the collections IDL file.

More on types and Generics

PHP 5 has introduced type hinting which allows functions to force parameters to be objects, interfaces, arrays or callable. HACK goes even further, it is statically typed and supports scalar types (i.e. int, num or string).

<?hh
require "/vagrant/www/xhp/php-lib/init.php";

set_error_handler(function ($no, $str) {
    $func = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
    $line = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['line'];
    echo <p>ERROR(calling "{$func}()" on l.{$line}) : {$str}</p>;
});

function echo_upper(string $s) {
    echo (strtoupper($s));
}

echo_upper("lower"); // LOWER
echo_upper(5); // ERROR(calling "echo_upper()" on l.16) : Argument 1 passed to echo_upper() must be an instance of string, int given
echo_upper(null); // ERROR(calling "echo_upper()" on l.17) : Argument 1 passed to echo_upper() must be an instance of string, null given

In this code sample, the echo_upper function expects a string as the first argument. Passing an int or null would trigger an error. null value could have been allowed by prepending a ? to the type, i.e. ?string.

HACK also brings support for generic programming via Generics similar to those of Java. For example you can specialize a regular array by specifying the type of its elements:

<?hh
function sum(array<int> $a) {
    return array_reduce($a, ($sum, $i) ==> $sum + $i);
}

echo <p>sum([1, 2, 3]) = {sum([1, 2, 3])}</p>; // sum([1, 2, 3]) = 6

Using Generics with built-in types is great but you can also implement your own types using Generics:

<?hh
class Generics<T as Countable> {
    private ?T $t;

    public function get() : ?T {
        return $this->t;
    }

    public function set(?T $t) {
        $this->t = $t;
    }

    public function __toString() {
        return var_export($this->t, true);
    }

    public function count() {
        return $this->t ? $this->t->count() : 0;
    }
}

$type = new Generics();
$type->set(Vector {1, 2, 3});

echo <div>$type
    <ul>
        <li>= {$type}</li> <!-- HH\Vector { 1, 2, 3, } -->
        <li>size = {$type->count()}</li> <!-- 3 -->
    </ul></div>;

This code sample is not really useful by itself but nevertheless it introduces a few interesting concepts. HHVM makes possible to:
– constrain the acceptable types: <T as Countable> means that the contained type must implement the \Countable interface which is a requirement dictated by the count() method,
– specify the property types, The $t property must be of T type. The leading ? means that $t could also be null,
– specify the return type of a method (or function), public function get() : ?T means that get will return a T type,
– constrain the argument, the set() method would only allow a T type which could possibly be null.

The sample code for this section is located at www/types/index.php and you can see its output by pointing your browser to http://localhost:8080/types/.

The HACK type system is really powerful and serves 2 purposes. The first one is to catch programming errors as early as possible and the second is to generate faster code (more on that in the next part).

What’s next

The next part of this article will show you more about HACK’s statically typed nature, how HACK makes asynchronous code execution easy, the XHP markup language and some more exciting features.