HHVM&Hack 相关简介
安装
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> $v、function f<T>(T $a): T ),类型约束的写法如 function f<T as num>(T $a): T。
Hack 提供了几个异步关键字,async、await、concurrent 等,这里不再具体介绍,具体使用参照下文。
示例
这里针对前面提到的几个特点写了几个示例程序。
<<__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(); });
两者输出及时序均相同。