HHVM and Hack: A quick introduction

44
HHVM + HACK A quick introduction

description

A quick look at Facebook's HipHop Virtual Machine and the Hack language.

Transcript of HHVM and Hack: A quick introduction

Page 1: HHVM and Hack: A quick introduction

HHVM + HACK A quick introduction

Page 2: HHVM and Hack: A quick introduction

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)

Page 3: HHVM and Hack: A quick introduction

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

Page 4: HHVM and Hack: A quick introduction

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

Page 5: HHVM and Hack: A quick introduction

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

Page 6: HHVM and Hack: A quick introduction

Installation

• Windows

• … just use Vagrant and Virtualbox

Page 7: HHVM and Hack: A quick introduction

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 runphp foobar.php

Page 8: HHVM and Hack: A quick introduction

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>

Page 9: HHVM and Hack: A quick introduction

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

Page 10: HHVM and Hack: A quick introduction

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;!}

Page 11: HHVM and Hack: A quick introduction

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

Page 12: HHVM and Hack: A quick introduction

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

Page 13: HHVM and Hack: A quick introduction

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

Page 14: HHVM and Hack: A quick introduction

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

Page 15: HHVM and Hack: A quick introduction

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

Page 16: HHVM and Hack: A quick introduction

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;! }!}

Page 17: HHVM and Hack: A quick introduction

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)

Page 18: HHVM and Hack: A quick introduction

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

Page 19: HHVM and Hack: A quick introduction

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

Page 20: HHVM and Hack: A quick introduction

Genericsclass 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

Page 21: HHVM and Hack: A quick introduction

Nullable typesclass 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;!}!

Page 22: HHVM and Hack: A quick introduction

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");

Page 23: HHVM and Hack: A quick introduction

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>

Page 24: HHVM and Hack: A quick introduction

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!}

Page 25: HHVM and Hack: A quick introduction

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 {}

Page 26: HHVM and Hack: A quick introduction

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" };!}

Page 27: HHVM and Hack: A quick introduction

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>>

Page 28: HHVM and Hack: A quick introduction

In other words, Hack really, really wants you to stop using simple arrays (or even ArrayObjects) as a “catch-all” data container

Page 29: HHVM and Hack: A quick introduction

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 scopeCan be an expression: function foo(): (function(string): string) {! $x = 'bar';! return $y ==> $y . $x;!}

Page 30: HHVM and Hack: A quick introduction

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

Page 31: HHVM and Hack: A quick introduction

Variadic functions

function sum(...): int {! $s = 0;! foreach (func_get_args() as $e) {! $s += $e;! }! return $s;!}!

• Cannot be type annotated, unfortunately

Page 32: HHVM and Hack: A quick introduction

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';! }!}

Page 33: HHVM and Hack: A quick introduction

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) {}!}

Page 34: HHVM and Hack: A quick introduction

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);

Page 35: HHVM and Hack: A quick introduction

Async and awaitasync 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);

Page 36: HHVM and Hack: A quick introduction

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

Page 37: HHVM and Hack: A quick introduction

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

Page 38: HHVM and Hack: A quick introduction

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

Page 39: HHVM and Hack: A quick introduction

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)

Page 40: HHVM and Hack: A quick introduction

• 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!

Page 41: HHVM and Hack: A quick introduction

• 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 {}

Page 42: HHVM and Hack: A quick introduction

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)

Page 43: HHVM and Hack: A quick introduction

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

Page 44: HHVM and Hack: A quick introduction

Thank you

Contact:

• Chris Heng <[email protected]>

• https://github.com/gigablah