Page 1
An Elephant of a Different Colour: Hack
Reduce your Server Load, Reuse your Code and Recycle your PHP Skills with HHVM & Hack
Image Copyright Keith Evans. This work is licensed under the Creative Commons Attribution-Share Alike 2.0 Generic Licence. To view a copy of this licence, visit http://creativecommons.org/licenses/by-sa/2.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San
Francisco, California, 94105, USA.
Page 2
Just in case you’re not up to speed on this HHVM
stuff…
© Some rights reserved by Dave Hamster
© Some rights reserved by Andy Barnes
© Some rights reserved by Casey Fleser
Page 3
HackXHP
Async
Collections
Lambda
Types
Page 4
XHP<?hhrequire_once('lib/init.php');$class = "greeting";$subject = "Toronto & area";$body = <p class={$class}>$hello {ucwords($subject)}</p>;echo <x:doctype> <html> <head><title>Hello XHP</title></head> <body>{$body}</body> </html></x:doctype>;
<!DOCTYPE html><html><head><title>Hello XHP</title></head><body> <p class="greeting">$hello Toronto & Area</p></body></html>
Page 5
XHP
XHP emits HTML without
the whitespace andformatting. I’ve added
itfor readability.
<?hhrequire_once('lib/init.php');$list = <ul />;for ($i = 0; $i < 7; $i += 1) { $list->appendChild(<li>{date('l', time() + $i * 86400)}</li>);}echo $list;
<ul> <li>Monday</li> <li>Tuesday</li> <li>Wednesday</li> <li>Thursday</li> <li>Friday</li> <li>Saturday</li> <li>Sunday</li></ul>
Page 6
XHP
• Available for Zend PHP too!
• See https://github.com/facebook/xhp
• Can use HTML() to emit unescaped HTML but don’t.
Page 7
CollectionsVector
Map
Set
Pair
0 => ‘Apples’,1 => ‘Bananas’,2 => ‘Oranges’
[Red] => ‘Apples’,[Yellow] => ‘Bananas’,[Orange] => ‘Oranges’
0 => ‘Apples’,1 => ‘Bananas’
‘Apples’,‘Bananas’‘Oranges’
Page 8
Vectors[0] => "zero"[1] => "one"[2] => 2
<?hh$v = Vector { "zero", "one" };
$v[] = 2;
$v->add($v->count());
$v->removeKey(0);
print_r($v);
[0] => “one"[1] => 2[2] => 3
[0] => "zero"[1] => "one"[2] => 2[3] => 3
[0] => "zero"[1] => “one”
Page 9
Old School Arrays
Can you spot the difference between
the Hack and PHP results?
<?php$v = array("zero", “one”);
$v[] = 2;
array_push($v, count($v));
unset($v[0]);
print_r($v); [1] => "one"[2] => 2[3] => 3
[0] => “zero"[1] => “one"[0] => "zero"[1] => "one"[2] => 2[0] => "zero"[1] => "one"[2] => 2[3] => 3
Page 10
Vectors
• Always 0 .. (count -1)
• Values are not hashed / indexed
• Use Sets for this
• Can use [] to append, but not unset() to remove
Page 11
PairPair Object( [0] => Pair Object ( [0] => Batman [1] => Robin ) [1] => xhp_p Object ( [tagName:protected] => p [attributes:xhp_x__composable_element:private] => Array () [children:xhp_x__composable_element:private] => Array ( [0] => The dynamic Duo ) [source] => /home/vic/prog/presentation/collections/pair.php:4 ))
<?hhrequire_once('../lib/init.php');$dynamicDuo = Pair {'Batman', 'Robin'};$p = Pair {$dynamicDuo, <p>The dynamic Duo</p>};print_r($p);
Page 12
Pair
• Pairs must contain two items of any type
• Pairs are always immutable
• Items are accessed with [0] and [1]
Page 13
Map
Map Object( [CA] => Canada [US] => United States [MX] => Mexico [NI] => Nicaragua)
<?hh$map = Map { 'CA' => 'Canada', 'US' => 'United States'};$map->add(Pair {'MX', 'Mexico'});$map['NI'] = 'Nicaragua';print_r($map);
Page 14
Map
• Maps contain key / value Pairs
• Keys can only be integers or strings
• Values can be of any type
• Order is preserved
Page 15
Set
HH\Set Object( US CA MX NI Has Canada)
• Can only contain integers orstrings
• Unordered
<?hh$s = Set {'US', 'CA'};$s->add('MX');$s[] = 'NI';$s[] = ($s->contains('CA') ? 'Has' : 'Lacks') . ' Canada';print_r($s);
Page 16
Types<?hhfunction add(int $a, int $b): int {
return $a + $b;}var_dump(add(1, 2));var_dump(add(1.5, 2));
int(3)HipHop Fatal error: Argument 1 passed to add() must be an instance of int, double given in /home/vic/prog/presentation/type.php on line 4
Page 17
Types<?hhfunction add(
shape('x' => int, 'y' => int) $p1,shape('x' => int, 'y' => int) $p2
): shape('x' => int, 'y' => int) {return shape(‘x' => $p1['x'] + $p2['x'], 'y' => $p1['y'] +
$p2[‘y']);}var_dump(add(['x' => 3, 'y' => 4], ['x' => 5, 'y' => 25]));
array(2) { ["x"]=>int(8) ["y"]=>int(29)}
Page 18
Types<?hhtype Coord = shape('x' => int, 'y' => int);function add(Coord $p1, Coord $p2): Coord {
return ['x' => $p1['x'] + $p2['x'], 'y' => $p1['y'] + $p2['y']];}var_dump(add(['x' => 3, 'y' => 4, 'z' => 1], ['x' => 5, 'y' => 25]));var_dump(add(['x' => 3, 'z' => 4], ['x' => 5, 'y' => 25]));array(2) {[“x”]=>int(8), [“y"]=>int(29)}
HipHop Notice: Undefined index: y in /home/vic/prog/presentation/typedef.php on line 4
array(2) {[“x”]=>int(8), ["y"]=>int(25)}
Page 19
Generics<?hhclass Accumulator<T> {
function __construct(private T $value) {}function add(T $value) { $this->value +=
$value; }function get():T { return $this->value; }
}$accumulateInts = new Accumulator<int>(5);$accumulateInts->add(4);var_dump($accumulateInts->get());
$accumulateFloats = new Accumulator<float>(0.7);$accumulateFloats->add(1.6);var_dump($accumulateFloats->get());
$accumulate = new Accumulator([5]);$accumulate->add([4,9]);var_dump($accumulate->get());
int(9)
float(2.3)array(2) {
[0]=>int(5) [1]=>int(9)}
Page 20
Generics<?hhclass Accumulator<T> {
function __construct(private T $value) {}function add(T $value) { $this->value +=
$value; }function get():T { return $this->value; }
}$accumulateInts = new Accumulator<int>(5);$accumulateInts->add(4);$accumulateInts->add(4.4);var_dump($accumulateInts->get());
float(13.4) !
Page 21
Nullable Types<?hhfunction printNum(?int $num) {
echo "num is ";echo is_null($num) ? "null" :
$num;echo "\n";
}printNum(1);printNum(null);printNum("Five");
num is 1num is nullHipHop Warning: Argument 1 to printNum() must be of type ?int, string given in /home/vic/prog/presentation/nullable.php on line 6num is Five
Page 22
Types<?hhclass MyClass<T as SomeInterface> {
function __construct(private T $v) {}}
newtype Secret as BaseClass = MyClass;
Page 23
Lambda<?php$countries = [
'US' => 'United States','CA' => 'Canada','MX' => 'Mexico',
];uasort($countries, function ($a, $b) {
return $a > $b;});var_dump($countries);
array(3) { ["CA"]=> string(6) "Canada" ["MX"]=> string(6) "Mexico" ["US"]=> string(13) "United States"}
Page 24
Lambda
array(3) { ["CA"]=> string(6) "Canada" ["MX"]=> string(6) "Mexico" ["US"]=> string(13) "United States"}
<?hh$countries = [
'US' => 'United States','CA' => 'Canada','MX' => 'Mexico',
];uasort($countries, ($a, $b) ==> $a > $b);var_dump($countries);
Page 25
Lambda
<?hh$add = ($a, $b) ==> $a + $b;
function curry($callable, $value) {return $v ==>
$callable($value, $v);}
$addFour = curry($add, 4);var_dump($addFour(5));
int(9)
Page 26
User Attributes<?hh<< MyAnnotation('Unicorn', 42) >>function bar() {}
$rc = new ReflectionFunction('bar');print_r($rc->getAttributes());
Array( [MyAnnotation] => Array ( [0] => Unicorn [1] => 42 )
)
Page 27
User Attributes
• You can add attributes / annotations to classes, methods, functions and arguments.
• You can specify more than one set of user attributes:<< Foo(1), Bar(2) >>
• You can’t use constants or variables in attributes
Page 28
Async
• I lied; hhvm’s async is more c# than node.
• Library functions like evhttp_async_get() don’t return Awaitable’s
• So they don’t seem to play well together
• PocketRent/beatbox uses async, but I don’t grock it
Page 29
class Foo{}
class Bar { public function getFoo(): Foo { return new Foo(); }}
async 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
async function gen_bar(int $a): Awaitable<?Bar> { if ($a === 0) { return null; }
return new Bar();}
gen_foo(4);
This is the exampleFacebook provided on GitHub for async.
Page 30
More• See hhvm.com
• The debugger is command line, and very good: hhvm -m debug
• Sara Golemon is giving a talk tomorrow night about HHVM and hack, and it will be live streamed:http://www.meetup.com/sf-php/events/159404382/
• Framework for Hack:https://wiki.pocketrent.com/beatbox/start
• I promise to blog about Hack at http://blog.vicmetcalfe.com/