HHVM and Hack: A quick introduction
-
Upload
kuan-yen-heng -
Category
Software
-
view
1.595 -
download
7
description
Transcript of HHVM and Hack: A quick introduction
HHVM + 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)
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
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
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
Installation
• Windows
• … just use Vagrant and Virtualbox
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
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>
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
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;!}
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
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
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
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
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
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;! }!}
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)
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
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
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
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;!}!
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");
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>
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!}
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 {}
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" };!}
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>>
In other words, Hack really, really wants you to stop using simple arrays (or even ArrayObjects) as a “catch-all” data container
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;!}
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
Variadic functions
function sum(...): int {! $s = 0;! foreach (func_get_args() as $e) {! $s += $e;! }! return $s;!}!
• Cannot be type annotated, unfortunately
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';! }!}
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) {}!}
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);
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);
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
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
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
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)
• 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!
• 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 {}
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)
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