安装

Arch Linux 下官方源没有 HHVM 的包,AUR 的包尝试发现有些许问题。参照 https://docs.hhvm.com/hhvm/installation/linux 这里编译安装即可。编译安装的默认目录是 /usr/local/bin,有

  • hackfmt
  • hh_client
  • hh_parse
  • hh_server
  • hh_single_compile
  • hhvm
  • hhvm-gdb
  • hhvm-repo-mode
  • hphpize

这么几个二进制文件。CPU 是 Intel Xeon E-2176M @ 12x 4.4GHz,编译安装总耗时大约三小时。

Hack

关于 HHVM 的前世今生参照维基百科即可,Hack 语言的特性也可参照官方文档,这里和 PHP7 对比一下,简单介绍一下发现的几个特点。

Hack 主要是有函数的参数/返回值类型声明和类内成员类型声明。前者是类似 function f(int $a): int。与 PHP 相比较,PHP5 开始支持对函数进行类型约束,即约定参数必须为对象、接口、array、callable 等,但并不支持诸如 int、float、string、bool 等的约束。PHP7 新增了对 int 之类的标量类型声明,以下代码声明了严格模式,由于类型不匹配会报错。

<?php
declare(strict_types=1);

function f(int $a): string {
return $a . $a;
}

var_dump(f('1'));

而后者指的是在 Hack 中支持类内成员 public int $a; 之类的写法,PHP7.4 即将支持(现在是 2019-07-05 )这样的写法。

Hack 内部对 PHP 万能的 array 做了划分,引入了 vec、dict、keyset 等容器,具体用法及性质与其他语言类似。同时由于类型的引入,Hack 也支持了泛型,使用与许多语言一样的 <> 符号来表示泛型,用来表示类型参数(如 public vec<T> $vfunction f<T>(T $a): T ),类型约束的写法如 function f<T as num>(T $a): T

Hack 提供了几个异步关键字,asyncawaitconcurrent 等,这里不再具体介绍,具体使用参照下文。

示例

这里针对前面提到的几个特点写了几个示例程序。


<<__EntryPoint>>
function main(): void {
$a = array(1, 2, 3);
$b = vec[1, 2, 3];
$c = dict['a' => 1, 'b' => 2, 'c' => 3];
var_dump($a, $b, $c);
}

输出如下:

array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
vec(3) {
int(1)
int(2)
int(3)
}
dict(3) {
[“a”]=>
int(1)
[“b”]=>
int(2)
[“c”]=>
int(3)
}


class Point
{
private float $x;
private float $y;

public function __construct(num $x = 0, num $y = 0)
{
$this->x = (float)$x;
$this->y = (float)$y;
}

public function __toString(): string
{
return "($this->x, $this->y)";
}
}

<<__EntryPoint>>
function main(): void {
$p = new Point(0.1, 0.2);
echo $p . PHP_EOL . PHP_EOL;
var_dump($p);
}

输出如下:

(0.1, 0.2)

object(Point) (2) {
[“x”:”Point”:private]=>
float(0.1)
[“y”:”Point”:private]=>
float(0.2)
}


final class Foo<T>
{
private T $x;

public function __construct(T $x)
{
$this->x = $x;
}
}

<<__EntryPoint>>
function main(): void {
$a = new Foo('a');
$b = new Foo(0);
var_dump($a, $b);
}

输出如下:

object(Foo) (1) {
[“x”:”Foo”:private]=>
string(1) “a”
}
object(Foo) (1) {
[“x”:”Foo”:private]=>
int(0)
}


async function get_async(int $x): Awaitable<int> {
// 1000000us, 1000ms, 1s
await SleepWaitHandle::create($x * 1000000);
return $x;
}

<<__EntryPoint>>
async function main(): Awaitable<void> {
var_dump(HH\Asio\join(get_async(1)));
concurrent {
$a = await(get_async(3));
$b = await(get_async(5));
}
echo $a + $b . PHP_EOL;
}

输出如下:

(等待 1 秒)

int(1)

(等待 5 秒)

8

这里对比 PHP 使用 Swoole4 扩展的写法。

<?php
Swoole\Runtime::enableCoroutine();

function get_async(int $x): int {
sleep($x);
return $x;
}

function main(): void {
var_dump(get_async(1));
$ch = new chan(2);
go(function () use ($ch) {
$ch->push(get_async(3));
});
go(function () use ($ch) {
$ch->push(get_async(5));
});
echo $ch->pop() + $ch->pop() . PHP_EOL;
}

go(function () { main(); });

两者输出及时序均相同。