Вы можете использовать ранее созданный блок Vagrant для запуска фрагментов кода из этой статьи.
Почему типы?
В первой части статьи мы видели, что HACK на самом деле был статически типизирован. Это означает, что вы должны предоставить типы для всех переменных в вашем приложении. Напоминаем, что PHP типизируется динамически, поэтому вам не нужно вводить переменные, хотя вы можете использовать подсказки типов для аргументов функций.
Но подождите, значит ли это, что вы должны предоставить типы для каждой переменной вашего приложения? Не совсем, и мы собираемся увидеть детали.
Кодовая база Facebook состоит из сотен миллионов строк кода, и добавление типов повсюду, прежде чем они смогут перейти на HACK, было бы настоящим бременем. Так что они пришли с «постепенной типизацией»: HACK ожидает, что типы только в «строгом» режиме. В нестрогом режиме типы будут учитываться только там, где они присутствуют.
Вход в строгий режим так же прост, как переключение начального тега HACK с <?hh
на <?hh // strict
.
Даже в строгом режиме вам не нужно аннотировать все переменные. Это потому, что HACK достаточно умен, чтобы выводить типы локальных переменных. Типовые аннотации требуются только для свойств класса, аргументов функций и возвращаемых значений. В противном случае я бы рекомендовал аннотировать локальные переменные, когда это может помочь в понимании вашего кода.
Давайте посмотрим на пример:
<? hh // strict
require "/vagrant/www/xhp/php-lib/init.php" ;
// ...
function add ( int $a , int $b ): int {
return $a + $b ;
}
// ERROR(calling "add()" on l.17) : Argument 2 passed to add() must be an
// instance of int, string given echo <p> add ( 1 , "a" ) = { add ( 1 , "a" )}</ p >;
// ERROR(calling "add()" on l.22) : Argument 2 passed to add() must be an
// instance of int, string given
function add_array ( array <int> $a ): int {
return array_reduce ( $a , "add" , 0 );
} echo <p> add_array ([ 1 , "a" ]) = { add_array ([ 1 , "a" ])}</ p >;
Пример кода для этого раздела находится по адресу www/type-checker/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/type-checker/
.
Первое сообщение об ошибке неудивительно: вызов add(1, "a")
генерирует ошибку, поскольку add()
ожидает, что второй аргумент будет целым числом.
Второе сообщение об ошибке является более неожиданным: ошибка не генерируется вызовом add_array([1, "a"])
. На самом деле это вызов add(1, "a")
внутри add_array()
который генерирует ошибку! Можно было ожидать, что передача [1, "a"]
вызовет ошибку, потому что это не array<int>
.
Дело в том, что проверка во время выполнения HHVM редка, чтобы не влиять на производительность: она не перебирает объекты. В этот момент вы, вероятно, сомневаетесь в полезности системы типов HACK! Но не волнуйтесь, есть простой ответ, «проверка типов»: он будет отлавливать любые несоответствия типов, в том числе из предыдущего примера. Не ищите его в репозитории HHVM, он еще не был выпущен Facebook.
Проверка типов реализована в виде сервера, который следит за изменениями в ваших файлах. Всякий раз, когда он обнаруживает изменение, он сканирует измененный файл вместе с зависимостями на наличие ошибок. Ошибки сообщаются в режиме реального времени, так что вам даже не нужно запускать код. Он был разработан, чтобы работать очень быстро, даже в масштабе FB.
Теперь вы должны быть уверены, что система типов работает отлично, но каковы преимущества? Это позволяет отлавливать ошибки разработчика в режиме реального времени, создавая более эффективный код: функция PHP add()
сначала должна проверить типы $a
и $b
(т. string
, null
,…), возможно, преобразовать их в числа и только потом выполнить сложение. Принимая во внимание, что с HACK функция add()
выше, добавляет два ненулевых целых числа, что является очень быстрой операцией на языке ассемблера (как генерируется JH HHVM).
Если в качестве разработчика вы уже используете подсказки типа PHP и аннотации PHPDoc , переключение в строгий режим должно быть простым. Ваш код станет безопаснее и быстрее — обратите внимание, что некоторые существующие инструменты QA, такие как Scrutinizer, уже используют определение типа для проверки вашего кода, хотя они не в реальном времени.
Если вы используете PHP в основном из-за его динамически типизированной природы, вы, вероятно, захотите придерживаться нестрогого режима.
Атрибуты пользователя
Использование аннотаций в мире PHP за последние годы резко возросло. Для тех, кто не знаком с аннотациями, это метаданные, которые вы можете добавить в классы, интерфейсы, признаки, переменные и функции / методы.
Doctrine ORM , вероятно, был одним из первых PHP-проектов, широко использовавших аннотации. Ниже приведен пример конфигурации модели из документации Doctrine :
<? php /** @Entity */
class Message
{
/** @Column(type="integer") */
private $id ;
/** @Column(length=140) */
private $text ;
/** @Column(type="datetime", name="posted_at") */
private $postedAt ;
}
PHP, в отличие от многих других языков, не имеет встроенной поддержки аннотаций. Тем не менее, библиотека аннотаций Doctrine широко используется для извлечения метаданных из Docblocks. RFC, предлагающий встроенную поддержку аннотаций в PHP, был отклонен еще в 2011 году.
Атрибуты пользователя — это реализация аннотаций на Facebook. Они заключены в <<
… >>
и их синтаксис немного отличается от аннотаций Doctrine:
<? hh require "/vagrant/www/xhp/php-lib/init.php" ;
<< UA ( 'klass' , [ 'type' => 'class' ]) >>
class klass {
protected $prop ;
<< UA ([ 'type' => 'function' ]) >>
public function funktion (<< Argument >> $arg ) {
}
} $rc = new ReflectionClass ( klass ); $rm = $rc -> getMethod ( 'funktion' ); $ra = $rm -> getParameters ()[ 0 ];
// On class
// array ( 'UA' => array ( 0 => 'klass', 1 => array ( 'type' => 'class', ), ), )
// On method
// array ( 'UA' => array ( 0 => array ( 'type' => 'function', ), ), )
// On argument
// array ( 'Argument' => array ( ), ) echo <div><h1> User annotations < /h1> <h2>On class</ h2 >< p >{ var_export ( $rc -> getAttributes (), true )}</ p >
<h2> On method < /h2><p>{var_export($rm->getAttributes(), true)}</ p >
<h2> On argument < /h2><p>{var_export($ra->getAttributes(), true)}</ p ></ div >;
Следует отметить, что пользовательские атрибуты, что неудивительно, доступны из API отражения. Также обратите внимание, что поддержка аннотирования свойств класса все еще должна быть реализована.
Пример кода для этого раздела находится по адресу www/attributes/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/attributes/
.
XHP
К настоящему времени вы должны иметь представление о том, что такое XHP, как мы его использовали, из первого примера кода этой статьи. Позвольте мне процитировать Facebook для более полного определения «XHP — это расширение PHP, которое расширяет синтаксис языка, так что фрагменты документа XML становятся действительными выражениями PHP». Обратите внимание, что XHP доступен как расширение PHP и что HHVM имеет встроенную поддержку.
С XHP вы можете использовать <h1>{$hello}</h1>
где вы должны использовать "<h1>$hello</h1>"
с ванильным PHP. Хотя предыдущий пример тривиален, XHP может предложить больше:
- это проверит вашу разметку так, что вы не сможете написать недопустимый HTML — подумайте об отсутствующих закрывающих тегах, опечатках в именах параметров,…
- он обеспечивает некоторый уровень контекстуального экранирования — поскольку движок знает, что вы рендеринге, он может соответствующим образом экранировать HTML и значения атрибутов, чтобы предотвратить атаки XSS ,
- Вы можете написать свои собственные теги, расширяя или упаковывая существующие теги.
Давайте посмотрим на пример:
<? hh require "/vagrant/www/xhp/php-lib/init.php" ; $examples = [
'hello' => 'Hello HHVM / HACK' ,
'promotion' => 'Constructor argument promotion' ,
'collections' => 'Collections' ,
'types' => 'Types and Generics' ,
'type-checker' => 'The type checker' ,
];
// The XHP validation should be disabled for better performance in production
//:x:base::$ENABLE_VALIDATION = false;
class : tuto : examples extends : x : element {
// examples, current are required attributes attribute array examples @required ; attribute string current @required ;
// forbid to explicitly add children children empty ;
protected function render () { $select = < select onchange = "window.location.href=window.location.pathname + '?ex=' + this.value" />;
foreach ( $this -> getAttribute ( 'examples' ) as $name => $label ) { $selected = $name === $this -> getAttribute ( 'current' ); $child = < option selected ={ $selected } value ={ $name }>{ $label }</ option >; $select -> appendChild ( $child );
}
return $select ;
}
} $folder = preg_replace ( '/[^-_a-z0-9]/' , '' , isset ( $_GET [ 'ex' ]) ? $_GET [ 'ex' ] : '' );
function getTheCode ( $folder ) {
// ...
} echo <html>
<head><title>"XHP generated index" < /title></ head >
<body>
< tuto : examples examples ={ $examples } current ={ $folder } />
{ getTheCode ( $folder )}
< /body></ html >;
Полный пример кода для этого раздела находится по адресу www/hhxhp/index.php
и вы можете увидеть его вывод, указав в браузере http://localhost:8080/hhxhp/
.
В этом примере мы начинаем с определения пользовательского <tuto:examples>
который будет отображать <select>
, это делается объявлением класса :tuto:examples
. Нашему пользовательскому тегу потребуются два атрибута, examples
и current
но недопустимо иметь детей ( children empty;
)
Поскольку мы расширяем :x:element
base :x:element
мы должны переопределить метод render()
чтобы вернуть нашу пользовательскую разметку как XHP.
Facebook использует язык XHP в качестве основы для своей библиотеки пользовательского интерфейса, которая в конечном итоге также может стать открытой.
Выполнение асинхронного кода
У меня были планы написать раздел об исполнении асинхронного кода после просмотра некоторых тестов в репозитории HHVM. Однако я не смог прийти с рабочим примером. Это может быть связано с моим небольшим пониманием темы или с тем фактом, что Facebook еще не выпустил весь связанный код. Я мог бы написать об этом, когда Facebook выпустит некоторую документацию.
Другие преимущества
В экосистеме HHVM есть много вещей, которые не были рассмотрены в этой статье, потому что я должен был сделать выбор, что включать, и потому что Facebook еще не выпустил весь код и документацию.
Несколько вещей, которые стоит упомянуть, это недавняя поддержка FastCGI и встроенный отладчик .
Facebook также продемонстрировал «FBIDE», веб-среду IDE с автоматическим заполнением, подсветкой синтаксиса, совместным редактированием и многим другим. Мы могли бы ожидать, что это будет доступно позже.
Внешние ресурсы
Вы можете найти больше информации в некоторых выступлениях и слайдах от команды Facebook, которые я использовал для подготовки этой статьи. Впервые я услышал о HACK, услышав лекцию «серьезно относясь к PHP» от Кейта Адамса и еще одну замечательную лекцию от Жюльена Верлаге. Хорошие слайды Сары Големон также были очень полезны для меня.
Вывод
Facebook стремится обеспечить равенство функций с PHP для HHVM. К концу прошлого года HHVM уже смогла пройти 98,5% модульных тестов для 20+ самых популярных платформ PHP. С тех пор ситуация немного улучшилась .
На сегодняшний день HHVM выполняет код PHP быстрее, чем PHP, при этом потребляя меньше памяти. Это будет существенным преимуществом в пользу HHVM, когда паритет в конечном итоге будет достигнут. Кроме того, вы можете начать внедрять HACK, чтобы получить еще большую производительность и повысить безопасность кода с помощью средства проверки типов — помните, что вам не нужно конвертировать всю базу кода сразу благодаря постепенной типизации и тому факту, что HACK и PHP совместимы.
Через несколько месяцев мы можем ожидать больше документации и инструментов от Facebook. Вы могли бы даже помочь, внеся свой вклад в проект на GitHub , также существует программа для наград.
Одной из проблем, о которых сообщает сообщество PHP и которая, вероятно, является основным препятствием для принятия, является отсутствие поддержки расширений PECL. Чтобы смягчить это, Facebook имеет инструмент, который может автоматически компилировать расширения PHP для цели HHVM; хотя вероятность успеха далеко не 100%. Другая вещь, которая может здесь помочь, заключается в том, что разработка расширения для HHVM намного проще, чем разработка для PHP.
Тот факт, что HHVM поддерживается одной только Facebook, и необходимость подписать CLA, прежде чем вносить вклад в HHVM, кажется другим проблематичным.
Я лично считаю, что значительная конкуренция — это отличная вещь для будущего PHP.
В заключение я хотел бы поблагодарить команду Facebook за удивительную работу, которую они проделали, и за то, что она открыта. Если вы хотели бы видеть больше статей SitePoint о HHVM и HACK в будущем, не стесняйтесь предлагать темы, добавив комментарий ниже.