Facebook начал работать над HipHop для PHP в 2008 году. Их целью было ускорить выполнение PHP, и первая версия проекта состояла из тандема HPHPc / HPHPi. HPHPc — это транспортер PHP-C ++, который использовался для развертывания кода на производственных серверах, а HPHPi был интерпретатором, используемым на этапах разработки и отладки.
HPHPc проделал хорошую работу по улучшению производительности, но это было не без проблем: поддержание синхронизации HPHPc и HPHPi оказалось громоздким, но некоторые различия между переносимым кодом и интерпретируемым по-прежнему существовали. Вот почему еще в 2010 году Facebook решил пойти по другому пути и создал HHVM — новую виртуальную машину, разработанную для замены Zend Engine, используемого PHP. К концу 2012 года HHVM достигла паритета производительности с прежним HPHPc и вскоре превзошла его .
HHVM предназначен для достижения как паритета с функциями Zend Engine, так и наилучших возможных характеристик. Facebook заявляет о 3–10-кратном увеличении скорости и 1/2 объема памяти, переключаясь с PHP + APC на HHVM. Конечно, это действительно зависит от приложения (10x для базы кода FB). Эта статья не будет сфокусирована на паритете и производительности, так как множество ресурсов уже доступно, проверьте блог HHVM или Google на предмет «hhvm benchmark» . Чтобы узнать больше о HipHop и HHVM в целом, прочитайте предыдущие статьи SitePoint об этом.
Вместо этого эта статья будет сфокусирована на HACK, который является развитием языка PHP, разработанного для обеспечения большей безопасности, повышения производительности и эффективности работы разработчиков. Обратите внимание, что HACK и PHP одинаково поддерживаются HHVM. Несмотря на то, что HACK используется в Facebook на всех производственных серверах, пока мало информации просочилось. В двух словах, HACK — это PHP6 от Facebook — он предлагает исправить большинство проблем с PHP сегодня, добавив при этом некоторые новые функции, такие как статическая типизация.
Привет хак
Не все инструменты и почти нет документации для HACK были выпущены на сегодняшний день. Однако последний доступный исходный код HHVM уже поддерживает HACK. Вы можете установить виртуальную машину Vagrant, чтобы начать экспериментировать с HACK и запустить фрагменты кода из этой статьи:
# Vagrant should be installed on your machine $ git clone https://github.com/vicb/hhvm-vagrant.git $ cd hhvm-vagrant $ vagrant up
Теперь вы готовы написать свою первую программу HACK:
<?hh require "/vagrant/www/xhp/php-lib/init.php"; $hello = "Hello HACK!"; echo <html> <head> <title>{$hello}!</title> </head> <body> <h1>{$hello}</h1> </body> </html>;
Этот образец доступен в вашем клоне бродячей виртуальной машины (находится по адресу www/hello/index.php
). Вы можете увидеть результат, указав в браузере http://localhost:8080/hello/
.
Приложение HACK запускается с <?hh
расположенного в самом начале исходного файла. Обратите внимание, что в HACK нет закрывающего тега. Этот пример довольно понятен, единственное, на что стоит обратить внимание , это то, что он использует XHP , язык разметки XML, который также доступен в качестве стандартного расширения PHP . Подробнее о XHP позже в этой статье.
Более интересный HACK добра будет введен в следующих разделах.
Продвижение аргумента конструктора
HACK не только более эффективен, чем стандартный PHP, когда речь идет о скорости выполнения, но также фокусируется на повышении эффективности работы разработчиков.
«Продвижение аргумента конструктора» — это одна из функций, помогающая сократить стандартный код. Действительно, общий шаблон при создании класса в PHP — сначала объявить свойства, а затем назначить их все прямо из аргументов конструктора. С HACK, добавление видимости в качестве модификатора аргумента конструктора достаточно как для объявления свойства, так и для его инициализации. Это делает код более лаконичным:
<?hh class PHPClass { public $pub; protected $pro; private $pri; public function __construct($pub, $pro, $pri) { $this->pub = $pub; $this->pro = $pro; $this->pri = $pri; } } // object(PHPClass)#6 (3) { ["pub"]=> string(3) "pub" ["pro":protected]=> string(3) "pro" ["pri":"PHPClass":private]=> string(3) "pri" } var_dump(new PHPClass('pub', 'pro', 'pri')); // The same class written in HACK is much more concise // thanks to constructor argument promotion. class HHClass { public function __construct(public $pub, protected $pro, private $pri) {} } // object(HHClass)#6 (3) { ["pub"]=> string(3) "pub" ["pro":protected]=> string(3) "pro" ["pri":"HHClass":private]=> string(3) "pri" } var_dump(new HHClass('pub', 'pro', 'pri'));
Пример кода для этого раздела находится по адресу www/promotion/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/promotion/
.
Обратите внимание, что Facebook предложил эту функцию для включения в состав PHP .
Коллекции
HACK добавляет поддержку коллекций. Некоторые из доступных классов покажутся знакомыми разработчикам PHP, хотя есть и долгожданные дополнения.
Типы Vector
и Map
являются расширенной версией массивов PHP в том смысле, что они предоставляют те же функциональные возможности, но предоставляют хороший объектно-ориентированный интерфейс, который отсутствует в стандартном PHP:
<?hh require "/vagrant/www/xhp/php-lib/init.php"; $v = Vector {"d", "c", "b"}; $v->add("a"); $v->reverse(); $v = $v->map(($_) ==> strtoupper($_))->filter(($_) ==> strcmp($_, "D") < 0); $items = <ul />; foreach ($v as $i) { $items->appendChild(<li>{$i}</li>); } echo <div>Vector:{$items}</div>; // A, B, C $m = Map {"a" => 1, "b" => 2}; $m->add(Pair {"d", 4}); echo <div>Map: <ul> <li>contains "a": {$m->contains("a") ? "yes" : "no"}</li> // yes <li>contains "c": {$m->contains("c") ? "yes" : "no"}</li> // no <li>size: {$m->count()}</li> // 3 </ul> </div>;
В этом примере вы также можете заметить введение «лямбды». Они являются сокращенной версией замыканий, ($args) ==> $result
является эквивалентом function($args) { return $result; }
function($args) { return $result; }
Пример кода для этого раздела находится по адресу www/collections/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/collections/
.
HACK также предоставляет тип Set
— набор уникальных элементов, «замороженный» неизменяемый вариант для каждого из вышеописанных типов (для дальнейшей оптимизации скорости), набор итераторов и множество полезных методов. Вы можете узнать больше, проверив IDL-файл коллекций .
Подробнее о типах и обобщениях
В PHP 5 введена подсказка типа, которая позволяет функциям заставлять параметры быть объектами, интерфейсами, массивами или вызываемыми. HACK идет еще дальше, он статически типизирован и поддерживает скалярные типы (то есть int
, num
или string
).
<?hh require "/vagrant/www/xhp/php-lib/init.php"; set_error_handler(function ($no, $str) { $func = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; $line = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['line']; echo <p>ERROR(calling "{$func}()" on l.{$line}) : {$str}</p>; }); function echo_upper(string $s) { echo (strtoupper($s)); } echo_upper("lower"); // LOWER echo_upper(5); // ERROR(calling "echo_upper()" on l.16) : Argument 1 passed to echo_upper() must be an instance of string, int given echo_upper(null); // ERROR(calling "echo_upper()" on l.17) : Argument 1 passed to echo_upper() must be an instance of string, null given
В этом примере echo_upper
функция echo_upper
ожидает string
в качестве первого аргумента. Передача int
или null
вызовет ошибку. null
значение можно было допустить, добавив ?
к типу, то есть ?string
.
HACK также обеспечивает поддержку универсального программирования с помощью Generics, аналогичного Java. Например, вы можете специализировать обычный array
, указав тип его элементов:
<?hh function sum(array<int> $a) { return array_reduce($a, ($sum, $i) ==> $sum + $i); } echo <p>sum([1, 2, 3]) = {sum([1, 2, 3])}</p>; // sum([1, 2, 3]) = 6
Использование Generics со встроенными типами прекрасно, но вы также можете реализовать свои собственные типы, используя Generics:
<?hh class Generics<T as Countable> { private ?T $t; public function get() : ?T { return $this->t; } public function set(?T $t) { $this->t = $t; } public function __toString() { return var_export($this->t, true); } public function count() { return $this->t ? $this->t->count() : 0; } } $type = new Generics(); $type->set(Vector {1, 2, 3}); echo <div>$type <ul> <li>= {$type}</li> <!-- HH\Vector { 1, 2, 3, } --> <li>size = {$type->count()}</li> <!-- 3 --> </ul></div>;
Этот пример кода не очень полезен сам по себе, но, тем не менее, он вводит несколько интересных концепций. HHVM позволяет:
— ограничить допустимые типы: <T as Countable>
означает, что содержащийся тип должен реализовывать интерфейс \Countable
который является требованием, определяемым методом count()
,
— укажите типы свойств. Свойство $t
должно иметь тип T. Ведущий ?
означает, что $t
также может быть нулевым,
— указать тип возврата метода (или функции), public function get() : ?T
означает, что get
вернет тип T,
— ограничивая аргумент, метод set()
допускает только тип T, который может быть нулевым.
Пример кода для этого раздела находится по адресу www/types/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/types/
.
Система типа HACK действительно мощная и служит 2 целям. Первый — как можно раньше выявлять ошибки программирования, а второй — генерировать более быстрый код (подробнее об этом в следующей части).
Что дальше
В следующей части этой статьи вы узнаете больше о статически типизированной природе HACK, о том, как HACK облегчает выполнение асинхронного кода, о языке разметки XHP и о некоторых более интересных функциях.