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.

Avatar for Chris Heng

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