Building a Better PHP — Part 1: HHVM and Hack

Building a Better PHP — Part 1: HHVM and Hack

With the official release of Hack for HHVM today by Facebook, we thought it would be a good idea to take a look at HHVM, and Hack in detail.

Facebook is [probably] the largest PHP installation on the planet, and yet in more recent years, they have turned away from PHP5 in favor of home-grown solutions.

At this point, can they even call themselves a PHP shop? The answer lies somewhere between "absolutely" and "are you crazy?".

A Brief History of HipHop

Facebook initially created HPHPc, a PHP to C++ compiler which would translate their huge codebase into C++ code and then compile it — this process took a long time (hours) and while it gave some performance gains, its negative impact on developers was even bigger.

Alongside, Facebook also created HPHPi (a developer mode that didn't require compiling the entire codebase every time) and HPHPd (a debugger).

HPHPc was in some ways a success for Facebook, giving necessary performance gains to allow them to scale beyond their previous abilities on the same hardware. But it was built against PHP 5.2, and could only support a subset of the language (for example, it did not support dynamic functions created using create_function() or the dreaded eval()).

In order to move forward, something new was needed — so in 2011, Facebook released the first public version of the HipHop Virtual Machine, or HHVM. They deprecated HPHPc (and its siblings) in favor of a fast-performing Just In Time compiler (JIT) that took PHP code and compiled it into Machine Code on the fly.

The initial release of HHVM was almost 100% compatible with PHP 5.4, and it continues to add support for new language features in PHP 5.5 and above.

The mantra you will hear from the team behind HHVM is "bug-for-bug parity". That is, while they could create a "perfect" PHP implementation, they are trying to recreate the same edge-cases and oddities of the PHP language so that the migration path is easier.

Having said that, they are committed to fixing actual bugs in PHP.net that are possible to fix without breaking backwards compatibility in a huge way.

You can see Facebooks effort to achieve parity by comparing popular open source test suite results between HHVM and PHP.net here.

HHVM vs PHP.net

HHVM is faster, much faster in a lot of cases. But speed isn't everything.

Most people focus on the performance improvements of HHVM over PHP.net as its primary differentiator, but there are definitely other things to consider.

Environment Impact

This isn't about carbon emissions… though, given the fact that HHVM can drastically reduce the number of servers and therefore power requirements, there is an argument that can be made for this too!

It is about the impact on your technical environment. With the release of HHVM 2.3 they added support for the FastCGI interface. This means that if you are currently using php-fpm, HHVM is effectively drop-in.

Additionally, it is possible to run PHP.net and HHVM simultaneously by simply routing some requests to different FastCGI proxies.

For sysadmins, if you are using Ubuntu 12.04/13.10, Debian 7, or Fedora 20, there are packages available directly from the HHVM team.

One of the biggest pain points for developers will be lack of IDE support — specifically for debugging. Some, may also not allow you to set the HHVM binary in-place of the PHP binary for running command line scripts and things like PHPUnit.

Perhaps the biggest deficiency at this point however is the lack of support for standard PHP extensions — this means that if you use a PECL extension, it probably won't work with HHVM out of the box. The team is actively trying to implement the most common extensions however, and making great progress in this. A list of currently available extensions can be found here.

Community Impact

There are some who believe that HHVM will fragment the PHP community — certainly, there is a possibility. However, the HHVM team are actively involved in (at least trying!) to contribute back to PHP.net.

An example of a feature that was first implemented in HHVM and then later in PHP is Generators, which were added in PHP 5.5.

Another example, the recent [rejected] RFC on arrayof syntax overlapped heavily with Generics, which are supported in HHVM's Hack language (more on that later), and the HHVM team (via Sara Golemon) participated heavily in the discussions around it.

Extensions

There has been a hugely renewed interest in PHP extensions lately, with tools like Zephir, and PHP-CPP, possibly spurred by the success of HHVM, but undoubtedly by the maturation of the community into the enterprise space over the last decade.

With HHVM, as long as the extension does not wrap a C/C++ library, then you can simply write it in PHP (or Hack). HHVM does such a good job of compiling down to machine code, it can be as fast, if not faster than writing the same thing in C++ yourself.

An example of this, is the community contributed Mongofill which is a Mongo extension replacement for HHVM.

In the case that you need to drop down to C++, HHVM provides the HHVM-Native Interface (HNI), which allows you to stub functions in PHP/Hack, and write the implementations in C++.

For full details on implementing extensions for HHVM, see the HHVM Documentation for the Extension API.

Hack?

While most people think of HHVM as an alternative PHP runtime, it is additionally the runtime for Facebooks own "language" known as Hack. Best described as a syntactical sibling of PHP that supports a number of new features such as scalar type hinting on both arguments and return values, generics, collections, constructor argument promotion, and more besides.

Hack is supported alongside standard PHP syntax, its major feature is in fact as a developer tool — when paired with the HHVM static analysis tool, you can get realtime information on things like type mismatches. Additionally, it allows HHVM to make more informed decisions when compiling the code.

However, once compiled, both Hack and PHP code are identical as far as HHVM is concerned.

Hack and HHVM are a great place to trial new potential features for PHP in general, and we can hope to see a lot of the great features back ported to PHP.net in the future.

And yes, it is possibly the most ungoogleable name ever.

Hack code starts with a <?hh open tag, rather than the standard <?php open tag, and does not use a closing tag. Hack cannot be interspersed with HTML like regular PHP (great for separation of concerns!) but does support native XHTML as a first-class syntax.

Note: The XHTML syntax can be added to PHP.net using the xhp extension.

<?hh
class Calculator {
    public function add(int $left, int $right): int {
        return $left + $right;
    }
}


$calc = new Calculator();
$calc->add(1, 3); // 4
$calc->add("one", "three"); // Fatal Error

Running this results in the fatal error:

Fatal error: Argument 1 passed to Calculator::add() must be an instance of int, string given in <file> on line 5

With HHVMs static analysis tool this error can be found without even running the code, even on non-common code paths.

We'll see more on Hack in Part 3 of this series.

Installing HHVM

Installing HHVM is as easy as booting a Vagrant VM, simply place the following in your VagrantFile:

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Use Ubuntu 12.04 LTS (64bit)
  config.vm.box = "hashicorp/precise64"
  config.vm.network "public_network" # Create a bridge network

  # Install HHVM
  config.vm.provision "shell", inline: <<-shell
    apt-get update
    apt-get install python-software-properties  -y --force-yes
    add-apt-repository ppa:mapnik/boost
    wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
    echo deb http://dl.hhvm.com/ubuntu precise main | sudo tee /etc/apt/sources.list.d/hhvm.list
    apt-get update
    apt-get install hhvm-nightly -y --force-yes
    apt-get install screen vim -y --force-yes
    debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password pa$$'
    debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password pa$$'
    apt-get install mysql-server -y --force-yes
  shell
end

Once you have your VagrantFile ready to go, simply issue a vagrant up command and wait.

After the machine boots you will be able to ssh in using vagrant ssh and play with HHVM.

Running HHVM

Once you have HHVM installed as above, you can simply call it using the hhvm command. For example, assuming our Calculator class is in calc.php we might run:

$ hhvm calc.php

In addition to simple command line usage, there are two ways to run HHVM for the web. The first, is the built-in server — this was the only way to run HHVM until recently, when the second option, FastCGI was added.

To use HHVM as FastCGI, it needs to sit behind a web server like nginx, similar to php-fpm. To install simply:

$ sudo apt-get install nginx
$ sudo mkdir /var/www
$ sudo chown vagrant:daemon /var/www

Next, add a new nginx config file to /etc/nginx/sites-available/hhvm.conf:

server {
  server_name hhvm.dev;

  root /var/www;
  index index.php;

  location ~ \.(hh|php)$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
    include fastcgi_params;
  }
}

And install it in place of the default:

$ sudo rm /etc/nginx/sites-enabled/default
$ sudo ln -s /etc/nginx/sites-available/hhvm.conf /etc/nginx/sites-enabled/hhvm.conf
$ sudo service nginx restart

Next, we setup HHVM to run as FastCGI by updating the /etc/hhvm/server.hdf (<= 2.4.*) or /etc/hhvm/server.ini (current nightlies, or >= 2.5.0) config file:

server.hdf

Server {
  Port = 9000
  Type = fastcgi
  SourceRoot = /var/www
}

Log {
  Level = Error
  UseLogFile = true
  File = /var/log/hhvm-error.log
  Access {
    * {
      File = /var/log/hhvm-access.log
      Format = %h %l %u %t \"%r\" %>s %b
    }
  }
}

Repo {
  Central {
    Path = /var/log/hhvm/.hhvm.hhbc
  }
}

server.ini

; php options

pid = /var/run/hhvm/pid

; hhvm specific

hhvm.server.port = 9000
hhvm.server.type = fastcgi
hhvm.server.source_root = /var/www
hhvm.server.default_document = index.php
hhvm.log.level = Error
hhvm.log.use_log_file = true
hhvm.log.file = /var/log/hhvm/error.log
hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc

Restart (or start) HHVM:

$ service hhvm restart

Note: prior to the HHVM 2.5/3.0 (or recent nightlies) the init scripts for Ubuntu (at least) were broken. Instead, you should use sudo killall hhvm && sudo service hhvm start to restart.

Finally, add an index.php to the webroot, /var/www and then access the web server (you can get the IP by calling ifconfig).

<?php
phpinfo();
?>

And in the case of HHVM, this will output simply:

HipHop

HHVM on Cloud

At Engine Yard, we constantly track new technologies, and have been tracking HHVM very closely for over a year. We are very excited by what we're seeing. While we do not yet support HHVM on our stack, we are working hard to integrate it into our PHP stack.

HHVM in the cloud will mean better utilization of cloud resources, allowing you to stretch your budgets further, and do more, with less.

Coming up next...

In the next part in this series, we will look at practical applications for HHVM that you can start using today — even if you're not ready to move your codebase over.

About Davey Shafik

Davey Shafik is a full time PHP Developer with 12 years experience in PHP and related technologies. A Community Engineer for Engine Yard, he has written three books (so far!), numerous articles and spoken at conferences the globe over.

Davey is best known for his books, the Zend PHP 5 Certification Study Guide and PHP Master: Write Cutting Edge Code, and as the originator of PHP Archive (PHAR) for PHP 5.3.