Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HHVM + Hack: A quick introduction

HHVM + Hack: A quick introduction

A brief overview of Facebook's HipHop Virtual Machine for PHP and Hack.

Chris Heng

April 30, 2014
Tweet

More Decks by Chris Heng

Other Decks in Programming

Transcript

  1. HHVM • Stands for HipHop Virtual Machine • Executes scripts

    written in PHP (5.4) and Hack • Drop-in replacement for php-fpm • Use together with FastCGI-enabled webserver (like Apache or nginx)
  2. Timeline • 2008: Facebook begins work on HipHop, a PHP

    to C++ compiler • 2010: Facebook opensources the compiler (HPHPc) and dev interpreter/ debugger (HPHPi and HPHPd) • 2010: Facebook begins work on HHVM • 2013: HPHPc deprecated, replaced by HHVM in Facebook production servers • 2013: HHVM 2.3.0 adds FastCGI support • 2014: HHVM 3.0.0 adds Hack support
  3. Installation • Ubuntu add-apt-repository -y 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" > /etc/apt/sources.list.d/hhvm.list! apt-get update! apt-get install hhvm! • Note: there is no more hhvm-fastcgi package. For the latest code, you can use hhvm-nightly • https://github.com/facebook/hhvm/wiki/Prebuilt-Packages-on- Ubuntu-12.04
  4. Installation • OSX (using Homebrew) brew tap homebrew/dupes! brew tap

    homebrew/versions! brew tap mcuadros/homebrew-hhvm! brew install hhvm --HEAD! • https://github.com/facebook/hhvm/wiki/Building-and- installing-HHVM-on-OSX-10.9 • Unfortunately, the typechecker (hh_server / hh_client) does not build on OSX
  5. Usage • Using from the command line
 hhvm foobar.php •

    Or you can make use of this:
 sudo /usr/bin/update-alternatives --install \
 /usr/bin/php php /usr/bin/hhvm 60 • Now you can run
 php foobar.php
  6. Usage • Apache 2.2: install libapache2-mod-fastcgi <IfModule mod_fastcgi.c>! Alias /hhvm.fastcgi

    /var/www/fastcgi/hhvm.fastcgi! FastCGIExternalServer /var/www/fastcgi/hhvm.fastcgi -socket /var/run/hhvm/socket -pass-header !! ! Authorization -idle-timeout 300! <Directory "/var/www/fastcgi">! <Files "hhvm.fastcgi">! Order deny,allow! </Files>! </Directory>! ! AddHandler hhvm-hack-extension .hh! AddHandler hhvm-php-extension .php! ! Action hhvm-hack-extension /hhvm.fastcgi virtual! Action hhvm-php-extension /hhvm.fastcgi virtual! </IfModule>
  7. Usage • Apache 2.4: mod_proxy + mod_proxy_fcgi
 ProxyPass / fcgi://127.0.0.1:9000/root/path

    • Or
 ProxyPassMatch ^/(.*\.(hh|php)(/.*)?)$ fcgi://127.0.0.1:9000/root/path/$1
  8. Usage • nginx: just like your regular php-fpm
 location ~

    \.(hh|php)$ {! root /path/to/your/root! fastcgi_pass 127.0.0.1:9000;! fastcgi_index index.php;! fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;! include /etc/nginx/fastcgi_params;! }

  9. Using with Composer • Add to .bashrc or equivalent:
 alias

    composer="hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 /usr/local/bin/composer" • Up to 5x faster
  10. Extensions • HHVM ships with several common extensions, listed here:


    https://github.com/facebook/hhvm/wiki/Extensions • Usually, to add an extension, you write it in pure PHP (e.g. the Redis extension) and place it alongside the source code, to be compiled together with HHVM • You may also use HNI (HHVM-Native Interface) for hybrid PHP/C++ implementations • Can be fashioned as an externally buildable DSO, e.g. mongofill
  11. Performance • Up to 2x faster compared to PHP 5.5


    (Sample benchmark performed by Christian Stocker with Symfony 2.4)
 http://blog.liip.ch/archive/2013/10/29/hhvm-and-symfony2.html
  12. Caveats • PHP 5.5 generator syntax incompatibility (#1627, #1787, #1871)

    • Missing PHP 5.4 Closure::bind and Closure::bindTo (#1203) • Cannot set multiple cookies with same name but different path (#2494, #2526) • func_get_args() returns arguments by references instead of values (#1027) (wontfix) • array_key_exists(), reset(), end(), etc. don't work with ArrayAccess (#1221) (wontfix) • missing fastcgi_finish_request() equivalent (#1230) • https://github.com/facebook/hhvm/issues?labels=php5+incompatibility&state=open
  13. Hack • Basically a superset of PHP, the most important

    addition being static typing • Hack files should begin with <?hh instead of <?php • Can be used in 3 modes: "strict", "partial" (default) and "decl", e.g. <?hh // strict • "// UNSAFE" annotation used to disable type checking for a particular block • Decl mode basically disables type checking for the entire file, enables strict mode to call into legacy code • Type annotations are thrown away at runtime
  14. Type annotations • This allows validation by the Hack typechecker

    before runtime • Paired with IDE support, can be helpful when refactoring code • Scalar type hinting is now possible
 class MyClass {! const int MyConst = 0;! private string $x = '';! ! public function increment(int $x): int {! $y = $x + 1;! return $y;! }! }
  15. Supported types • Primitives: int, float, string, bool, array •

    User-defined classes: Foo, Vector<some type> • Mixed: mixed • Void: void • Nullable: ?<type> (e.g., ?int, ?bool) • Soft: @<type> (e.g. @int, @bool) • Typed arrays: array<Foo>, array<string, array<string, Foo>> • Tuples: (<type1>, <type2>, ....) e.g. (string, int)
  16. Supported types • XHP elements :x:frag, :x:base, :div, :xhp •

    Generics: Foo<T> • Closures: (function(<type1>, <type2>, ...): return_type)
 e.g. (function(string, int): string) • Resources: resource • Continuation (generator): Continuation<T> • Awaitable (async): Awaitable<T> • Same type: this
  17. Supported types • Note that: • Untyped arrays are not

    allowed in strict mode • Do not annotate return values for __construct() (typechecker will complain) • "this" only checks for same type, not same instance
  18. Generics class Box<T> {! protected T $data;! ! public function

    __construct(T $data) {! $this->data = $data;! }! public function setData(T $data): void {! $this->data = $data;! }! public function getData(): T {! return $this->data;! }! }! ! function swap<T>(Box<T> $a, Box<T> $b): T {! $temp = $a->getData();! $a->setData($b->getData());! $b->setData($temp);! return $temp;! }! • For each instance, once a type is associated with T, it’s fixed
  19. Nullable types class NullableBox<T> {! protected ?T $data;! ! public

    function __construct(?T $data) {! $this->data = $data;! }! ! public function getData(): ?T {! return $this->data;! }! }! ! $box = new NullableBox(null);! ! ! function check_not_null(?int $x): int {! return $x === null ? -1 : $x;! }!
  20. Soft types • Apparently not documented • Emits warnings rather

    than fatal errors for invalid types ! ! class Calculator {! public function add(@int $a, @int $b): @string {}! }! ! $calc = new Calculator();! $calc->add("1", "2");
  21. Typed arrays • Untyped arrays are only allowed in partial

    or decl mode • In strict mode you must explicitly type the values: array<Foo>! • Or both keys and values: array<string, ?string>! • You can simulate PHP behaviour, but this defeats the purpose: array<mixed, mixed>
  22. Type aliasing • Redefine an existing type name type MyInteger

    = int;! • “Opaque” types disallow other files from accessing the underlying implementation (e.g. concatenation for strings) // File1.php! newtype MyString = string;! ! // File2.php! require_once "File1.php";! ! function modifyString(MyString $s): MyString {! return $s . "1"; // Hack type checker will throw an error! }
  23. Tuples and shapes • Tuples are basically immutable typed arrays

    public function baz(): (string, int) {! return tuple("Hello", 3);! }! • Shapes are kinda like structs type MyShape = shape('id1' => string, 'id2' => int);! function foo(MyShape $x): void {}
  24. Collections • Vector, Map, Set, Pair • Immutable versions: ImmVector,

    ImmMap, ImmSet • Can be initialised using literal syntax $vec = Vector {'foo', 'foo', 'bar'}; // integer keys! $map = Map {42 => 'foo', 73 => 'bar', 144 => 'baz'}; // ordered dictionary! $set = Set {'php', 'hack', 'hhvm'}; // unordered unique values! $par = Pair {'guid', 'ABC123'}; // only two pieces of data! • Type annotation function getTags(): Set<string> {! return Set { "php", "hack", "hhvm" };! }
  25. Collections • Some method signatures for Vector… public function __construct(?Traversable<Tv>

    $it)! public function add(Tv $value): Vector<Tv>! public function addAll(?Traversable<Tv> $it): Vector<Tv>! public function at(int $k): Tv! public function clear(void): Vector<Tv>! public function containsKey(int $k): bool! public function count(void): int! public function filter((function(Tv): bool) $callback): Vector<Tv>! public function filterWithKey((function(int, Tv): bool) $callback): Vector<Tv>! public function fromArray(array $arr): Vector<Tv>! public function fromItems(?Traversable<Tv> $items): Vector<Tv>! public function get(int $k): ?Tv! public function getIterator(void): KeyedIterator<int, Tv>! public function isEmpty(void): bool! public function items(void): Iterable<Tv>! public function keys(void): Vector<int>! public function reverse(void): void! public function set(int $k, Tv $v): Vector<Tv>! public function setAll(?KeyedTraversable<int, Tv> $it): Vector<Tv>! public function __toString(void): string! public function toValuesArray(void): array! public function zip<Tu>(Traversable<Tu> $iterable): Vector<Pair<Tv, Tu>>
  26. In other words, Hack really, really wants you to stop

    using simple arrays (or even ArrayObjects) as a “catch-all” data container
  27. Lambdas • Use the lambda arrow ==>
 (Because -> and

    => are already taken): function concat(): (function(string, string): string) {! return ($x, $y) ==> {! return $y . $x;! };! }! • Implicitly capture variables from outer scope
 Can be an expression: function foo(): (function(string): string) {! $x = 'bar';! return $y ==> $y . $x;! }
  28. Lambdas • Type hints are not checked at the moment

    • Doesn't support capturing variables by reference or returning by reference yet • Lambda arrow operator is right associative and can be chained: ! $f = $x ==> $y ==> $x + $y;! $g = $f(7);! echo $g(4); // 11
  29. Variadic functions function sum(...): int {! $s = 0;! foreach

    (func_get_args() as $e) {! $s += $e;! }! return $s;! }! • Cannot be type annotated, unfortunately
  30. Override attribute • Signify that the parent method must exist

    class Hello {! public function render(): void {! echo 'hello';! }! }! ! class HelloWorld extends Hello! {! <<Override>> public function render(): void {! parent::render();! echo ' world';! }! }
  31. Constructor arg promotion class Person {! private string $name;! private

    int $age;! ! public function __construct(string $name, int $age) {! $this->name = $name;! $this->age = $age;! }! }! • can be written as ! class Person {! public function __construct(private string $name, private int $age) {}! }
  32. Callbacks • This will not work in strict mode: $func

    = 'myFunc';! $x = $func(4);! • fun() will return a callable, which allows the typechecker to properly validate: $func = fun('myFunc');! $x = $func(4);! • Similar dubiously-named functions exist for calling static/instance methods: // $bar = array_map(array('Foo', ‘doStuff'), [1, 2, 3]);! $bar = array_map(class_meth('Foo', 'doStuff'), [1, 2, 3]);! ! $foo = new Foo();! $bar = array_map(instance_meth($foo, 'doStuff'), [1, 2, 3]);! ! $func = meth_caller('Foo', 'doStuff');! $foo = new Foo();! $x = $func($foo);
  33. Async and await async function gen_foo(int $a): Awaitable<?Foo> {! if

    ($a === 0) {! return null;! }! $bar = await gen_bar($a);! if ($bar !== null) {! return $bar->getFoo();! }! return null;! }! ! async function gen_bar(int $a): Awaitable<?Bar> {! if ($a === 0) {! return null;! }! return new Bar();! }! ! gen_foo(4);
  34. Running the type checker • Run hh_client (it will launch

    hh_server for you) or hh_server --check . • Recursively looks for directories that contain a .hhconfig file (it won’t run if this is missing) • Optionally return output in json format • Note: does not follow symlinks
  35. Unsupported features • Still runs in HHVM, but the type

    checker does not like the following: • goto, if: … endif; etc • AND, OR, XOR • References (&) • Error suppression (@) • break N, continue N • eval() • globals
  36. Unsupported features • list(, $b) = array(3, 4); should be

    list($_, $b) = array(3, 4); • Function calls are case-sensitive • Strict mode does not support top-level code except for require() and require_once(); i.e. everything else must be in a class or function • Does not support "variable variables" (e.g. $$a) or extract() • Cannot call parent::staticMethod() • Cannot declare a class method with a name that collides with class name • Cannot pass primitives by reference
  37. Drawbacks • Still new, not yet 100% compatibility (but catching

    up fast) • Your favourite extension may not be available • Limited IDE support at the moment (weekend project idea?) • Hack-exclusive libraries? Package repos? Community fragmentation? • Clashes with PHP 5.5 and future 5.6 syntax
 (generators and variadic functions)
  38. • Generator functions are of the type “Continuation” in HHVM

    (since they were implemented earlier) • The following is valid in PHP 5.5 but not in HHVM: $data = (yield $value); // delete the brackets for HHVM! $data = yield; // HHVM needs expression after yield! • HHVM Continuations need to be “primed” unlike PHP 5.5 Generators $generator = construct_generator();! $generator->current(); // must call next() first in HHVM!
  39. • Proposed PHP 5.6 syntax for variadic functions: function fn($arg,

    ...$args) {}! function fn($arg, &...$args) {}! function fn($arg, callable ...$args) {}! • Type-hinting and arguments by value not supported in HHVM / Hack function fn(...): void {}
  40. Benefits • Type safety!!! (Always use the type checker) •

    Syntactic sugar from other languages • Discard “bad” parts of PHP • Active community (e.g. New Relic has released a prototype HHVM extension, Heroku just announced HHVM support)
  41. Resources • http://hhvm.com/ • http://hacklang.org/ • http://docs.hhvm.com/manual/en/hacklangref.php • https://blog.engineyard.com/2014/hhvm-hack •

    http://blog.vjeux.com/2014/javascript/hack-is-to-php-what-es6-is-to- javascript.html • https://github.com/facebook/hhvm