[BulgariaPHP 2015] Writing Faster PHP with HHVM and Hack

Fee39f0c0ffb29d9ac21607ed188be6b?s=47 Davey Shafik
September 26, 2015

[BulgariaPHP 2015] Writing Faster PHP with HHVM and Hack

HHVM is the new hotness, a super-fast alternative PHP runtime from Facebook it can take your existing PHP code base and run it at blazing fast speeds... but is there more?

HHVM also introduces Hack. Hack adds numerous features to the PHP language that help speed up both development time and runtime performance.

Get the most out of your human and technical resources by using HHVM and Hack today!

Fee39f0c0ffb29d9ac21607ed188be6b?s=128

Davey Shafik

September 26, 2015
Tweet

Transcript

  1. W R I T I N G FA S T

    E R P H P W I T H H H V M A N D H A C K
  2. D AV E Y S H A F I K

    • Author of Zend PHP 5 Certification Study Guide, Sitepoints PHP Anthology: 101 Essential Tips, Tricks & Hacks & PHP Master: Write Cutting Edge Code • A contributor to Zend Framework 1 & 2, phpdoc, & PHP internals • Original creator of PHAR/PHP_Archive • @dshafik
  3. h t t p : / / d e v

    e l o p e r. a k a m a i . c o m
  4. Let’s start a conversation about mental health in tech mhprompt.org

  5. A B R I E F H I S T

    O RY O F H I P H O P V M B E T T E R K N O W N A S H H V M
  6. H P H P C 2 0 0 9 -

    2 0 1 2
  7. H P H P C — 2 0 0 9

    - 2 0 1 2 • Cross-compiler: PHP -> C++ • Needed to compile for every change, which took hours • Created huge binaries (approaching 2GB Linux “limit”) • PHP 5.2 (ish) feature-set • Missing support for large parts of the language (e.g. all 5.3+ features including namespaces, closures, and dynamic features like create_function(), and eval()) • Broke the developer workflow
  8. H I P H O P V M ( H

    H V M ) 2 0 1 3 -
  9. H H V M • Alternate interpreted runtime with JIT

    compiler • No compiling process • Twice as fast as PHP in interpreted mode. • Ludicrous speed for JITed code • Bug-for-bug compatible with PHP • 5.6 feature set (e.g. support for variadics, splat) • Working on PHP 7 support
  10. H A C K A S U P E R

    - S E T O F P H P
  11. H A C K • AKA Hacklang (because “Hack” is

    un-google-able) • A syntactic super-set of PHP that allows for strong typing via static analysis. Adds support for: • type hinting • generics • async functions • annotations • more… • A language sub-set of PHP that disallows a number of bad practices • variable-variables • globals • mixing HTML & PHP • more…
  12. P E R F O R M A N C

    E
  13. 0 75 150 225 300 Wordpress 3.9 6.5.16 - login

    page pal 7.28 homepage ME app home page 2.3.1 Skeleton App ento 1.9 Homepage ento 1.9 item page PHP 5.4 (no opcache) PHP 5.5 PHP 5.6 hhvm 3.1 (prebuilt binaries) hhvm 3.2-dev(src) PHPNG Source: Zeev Suraski http://zsuraski.blogspot.com/2014/07/benchmarking-phpng.html
  14. Normalized Throughput 0 1 2 3 4 Drupal 7 Drupal

    8 Drupal 8 (cached) MediaWiki Wordpress Source: HHVM Team http://hhvm.com/blog/9293/lockdown-results-and-hhvm-performance
  15. A C T U A L U S E R

    S • Facebook (#2) • Baidu (#5, #1 Search Engine in China) • Wikimedia (incl. Wikipedia, #6) • Etsy (for their API) • Box.net That’s 3 of the top 4 PHP sites in the world, and ~1/3 of the top 10 overall. (Yahoo! is the outlier, FYI)
  16. L U D I C R O U S S

    P E E D ! R E P O A U T H O R I TA T I V E M O D E
  17. R E P O A U T H O R

    I TAT I V E M O D E • When code “never” changes (e.g. production) • Extra 20-25% performance increase (similar to apc.stat = 0) • Spends extra time on caching/JITing for maximum performance
  18. H H V M S E T U P

  19. H H V M S E T U P Nginx

    php-fpm mysql,pgsql,mongo,
 gearman,redis, etc. index.php
  20. H H V M S E T U P Nginx

    mysql,pgsql,mongo,
 gearman,redis, etc. index.php HHVM
  21. H H V M S E T U P server

    { server_name _; root /var/www; index index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/hhvm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name; include fastcgi_param; } }
  22. H H V M S E T U P $

    hhvm -m daemon -c /etc/hhvm/hhvm.ini -u www-data hhvm.server.file_socket = /var/run/hhvm.sock hhvm.server.type = fastcgi hhvm.server.source_root = /var/www hhvm.log.level = Error hhvm.log.user_log_file = true hhvm.log.file = /var/log/hhvm-error.log hhvm.log.access.0.file = /var/log/hhvm-access.log hhvm.log.access.0.format=%h %l %u %t \"%r\" %>s %b
  23. L O N G T E R M S U

    P P O R T
  24. L O N G T E R M S U

    P P O R T V E R S I O N I N T E N D E D R E L E A S E LT S ? E N D O F 3 . 3 1 1 1 9 S E P 2 0 1 4 Y E S 1 3 1 9 A U G 2 0 1 5 3 . 4 6 N O V 2 0 1 4 3 . 5 1 J A N 2 0 1 5 3 . 6 2 6 F E B 2 0 1 5 Y E S 2 8 J A N 2 0 1 6 3 . 7 2 3 A P R 2 0 1 5 3 . 8 1 8 J U N 2 0 1 5 3 . 9 1 3 1 9 A U G 2 0 1 5 Y E S 1 4 J U L 2 0 1 6 3 . 1 0 8 O C T 2 0 1 5 3 . 1 1 3 D E C 2 0 1 5 3 . 1 2 2 8 J A N 2 0 1 6 Y E S 2 9 D E C 2 0 1 6 3 . 1 3 2 4 M A R 2 0 1 6 3 . 1 4 * 1 9 M AY 2 0 1 6 3 . 1 5 * 1 4 J U L 2 0 1 6 Y E S 1 5 J U N 2 0 1 7
  25. H A C K A B E T T E

    R P H P
  26. – D AV E Y S H A F I

    K “A primary goal of hack is to not [negatively] impact the developers workflow — especially the REPL; whereby we can edit code and refresh our browser to immediately see changes.” H A C K
  27. H A C K • Not a new language •

    Runs through the same HHVM runtime • Strongly typed… but thrown away at run time (type erasure) • Super-fast static analysis • Strict typing informs the runtime to enable better performance
  28. E D I T O R S U P P

    O R T
  29. E D I T O R S U P P

    O R T S U P P O R T S A N Y E D I T O R ( S O L O N G I T ’ S V I M O R E M A C S )
  30. None
  31. !

  32. None
  33. Nuclide

  34. Sublime Text 3

  35. F I L E S E M A N T

    I C S
  36. F I L E S E M A N T

    I C S • To promote best practices, you cannot mix Hack with HTML (or other non- code text) • All hack files must start with <?hh • There is no closing tag for hack files • Hack also has multiple modes • XHTML can be embedded using XHP, which makes XHTML tags into language constructs
  37. H A C K M O D E S

  38. PA R T I A L M O D E

  39. PA R T I A L M O D E

    • Default • Gradual Typing • Types are strictly adhered to however: • Not everything must be typed • May call into regular PHP code without error
  40. PA R T I A L M O D E

    <?hh // Code goes here
  41. S T R I C T M O D E

  42. S T R I C T M O D E

    • All code must be type hinted • Cannot call into non-Hack code • No top-level code • This means your codebase cannot be 100% strict • Arrays must be type hinted • Collections (Vector, Set, Map, Tuple) are preferred
  43. S T R I C T M O D E

    <?hh // strict // Code goes here
  44. D E C L A R AT I O N

    S M O D E A K A D E C L
  45. D E C L A R AT I O N

    S M O D E • Type hints are trusted but not required
  46. D E C L A R AT I O N

    S M O D E <?hh // decl // Code goes here
  47. I G N O R I N G E R

    R O R S
  48. I G N O R I N G E R

    R O R S • You can ignore errors by marking code as UNSAFE • Applied from the comment to the end of the current block: typically the next curly brace “}” • Should be the last resort • May result in run-time errors due to the lack of validation // UNSAFE
  49. T Y P E H I N T S

  50. T Y P E H I N T S •

    Method Arguments • Return Values • Class Properties
  51. T Y P E S T Y P E B

    O O L B O O L E A N T R U E / FA L S E T Y P E N A M E I N T I N T E G E R N U M B E R S F L O A T F L O AT I N G P O I N T N U M B E R S S T R I N G S T R I N G S A R R A Y A R R AY S ( T H AT C A N B E T Y P E D ) R E S O U R C E R E S O U R C E S ( E . G . F I L E S T R E A M S ) C L A S S / I N T E R F A C E N A M E A N O B J E C T T Y P E - H I N T
  52. T Y P E S ( C O N T.

    ) T Y P E N A M E M I X E D A N Y ( N O T R E C O M M E N D E D ) V E C T O R N U M E R I C A L C O N T I G U O U S LY I N D E X E D A R R AY S M A P A T Y P E D ( B O T H K E Y S A N D VA L U E S ) A S S O C I AT I V E A R R AY S E T A N U N I N D E X E D C O L L E C T I O N O F T Y P E D U N I Q U E VA L U E S T U P L E A F I X E D S I Z E S E T O F T Y P E D VA L U E S P A I R A F I X E D S I Z E S E T O F T Y P E D VA L U E S R E S T R I C T E D T O T W O VA L U E S , I N D E X E D A S 0 A N D 1 .
  53. S P E C I A L T Y P

    E S • void — for functions with no return value • this — always refers to $this
  54. N U L L A B L E T Y

    P E S
  55. N U L L A B L E T Y

    P E S • Allows for nulls to be passed in addition to the specified type • Prefix the type with a ? • e.g. ?int or ?\DateTime.
  56. N U L L A B L E T Y

    P E S <?hh
 class DBAdapter { public function connect(?string $dsn): ?\PDO { // $dsn may be null // may return an instance of \PDO
 // or null on error } }
  57. S O F T T Y P E S

  58. S O F T T Y P E S •

    You can denote the a type-failure should not error by using soft types. • This is great for migrating codebases • To do this precede the type with an @ • e.g. @int or @\DateTime.
  59. S O F T T Y P E S <?hh

    class Calculator { public function add(@int $a, @int $b): @int { // Both $a and $b may not be ints // May not return an int } } $calc = new Calculator(); $calc->add("1", "2");
  60. C U S T O M T Y P E

    S
  61. C U S T O M T Y P E

    S • Type Aliases (type) • Opaque Types (newtype) • Only used for static analysis.
  62. T Y P E A L I A S E

    S
  63. T Y P E A L I A S E

    S • Allows you to give more appropriate names to existing types • e.g. unixtime for int
  64. <?hh // strict type HTTPStatusCode = int; class HTTPStatus {

    const HTTPStatusCode OK = 200; const HTTPStatusCode FOUND = 302; const HTTPStatusCode NOT_FOUND = 404; protected Map<HTTPStatusCode, string> $status = Map { self::OK => "200 OK", self::FOUND => "302 Found", self::NOT_FOUND => "404 Not Found", }; public function send(HTTPStatusCode $code): bool { if (isset($this->status[$code])) { header('HTTP/1.1 ' .$this->status[$code]); return true; } return false; } }
  65. <?hh // strict type HTTPStatusCode = int; class HTTPStatus {

    const HTTPStatusCode OK = 200; const HTTPStatusCode FOUND = 302 const HTTPStatusCode NOT_FOUND =
  66. <?hh // strict type HTTPStatusCode = int; class HTTPStatus {

    const HTTPStatusCode OK = 200; const HTTPStatusCode FOUND = 302; const HTTPStatusCode NOT_FOUND = 404; protected Map<HTTPStatusCode, string> $ self::OK => "200 OK", self::FOUND => "302 Found",
  67. class HTTPStatus { const HTTPStatusCode OK = 200; const HTTPStatusCode

    FOUND = 302; const HTTPStatusCode NOT_FOUND = 404; protected Map<HTTPStatusCode, string> $status = Map { self::OK => "200 OK", self::FOUND => "302 Found", self::NOT_FOUND => "404 Not Found", }; public function send(HTTPStatusCode $code): bool { if (isset($this->status[$code])) { header('HTTP/1.1 ' .$this->status[$code]);
  68. self::OK => "200 OK", self::FOUND => "302 Found", self::NOT_FOUND =>

    "404 Not Found", }; public function send(HTTPStatusCode $code): bool { if (isset($this->status[$code])) { header('HTTP/1.1 ' .$this->status[$code]); return true; } return false; } }
  69. <?hh function notFound() { $status = new HTTPStatus(); $status->send(404); }

  70. <?hh function notFound() { $status = new HTTPStatus(); $status->send(HTTPStatus::NOT_FOUND); }

  71. O PA Q U E T Y P E S

  72. O PA Q U E T Y P E S

    • Opaque types hide their underlying implementation outside of the file in which the type is defined. • This means they become immutable except when called in code defined in that file • This restriction is not enforced at runtime
  73. newtype HTTPStatusCode = int;

  74. <?hh function notFound() { $status = new HTTPStatus(); $status->send(404); }

    a.hh:6:19,21: Invalid argument (Typing[4110]) b.hh:15:26,39: This is an object of type HTTPStatusCode b.hh:6:19,21: It is incompatible with an int
  75. <?hh function notFound() { $status = new HTTPStatus(); $status->send(HTTPStatus::NOT_FOUND); }

  76. C O N S T R U C T O

    R A R G U M E N T P R O M O T I O N
  77. C O N S T R U C T O

    R P R O P E R T Y A S S I G N M E N T <?php class Adder { private $left; private $right; public function __construct($left, $right) { $this->left = $left; $this->right = $right; } }
  78. C O N S T R U C T O

    R A R G U M E N T P R O M O T I O N • Automatically create properties from constructor arguments • Just precede the argument with a visibility keyword: public, private, protected
  79. C O N S T R U C T O

    R A R G U M E N T P R O M O T I O N <?hh class Adder { public function __construct(
 private int $left,
 private int $right
 ): void { } public function get(): int { return $this->left + $this->right; } }
  80. C O L L E C T I O N

    S
  81. C O L L E C T I O N

    S • Arrays — must be typed in strict mode • Tuples — typed arrays • Pairs — can only contain 2 pieces of data • Vectors — numerically, consecutively keyed only • Maps — ordered dictionaries that allow int or string keys • Sets — unordered collection of unique values • Immutable variants
  82. A R R AY S

  83. A R R AY S • array — Untyped (only

    allowed in partial mode) • array<[type]> — [type] typed values, with integer keys • array<[type1], [type2]> — [type1] typed keys, with [type2] typed values. <?hh // strict function createArray(): array<int> { return array(1, 2, 3); }
  84. T U P L E S

  85. T U P L E S • Internally identical to

    arrays • Fixed size • Implicitly Type hinted by the values used to define it or • Explicitly Type hinted using the literal syntax <?hh
 function createTuple(): (int, int, string) { return tuple(1, 2, "3"); }
  86. PA I R S

  87. PA I R S • Can contain only two values,

    keyed as 0 and 1 • Immutable <?hh // strict function getTask(): Pair<string, string> { return Pair { "C039D17D", "checkPing" }; }
  88. V E C T O R S

  89. V E C T O R S • Vectors can

    only use integer keys <?hh // strict function getDevEvangelist(): Vector<string> { return Vector {"Davey", "Kirsten", "You?"}; }
  90. M A P S

  91. M A P S • Maps are an ordered dictionary,

    which can have integer or string keys. • Must specify both key and values types when type hinting. <?hh // strict function getTags(): Map<string, string> { return Map {"php" => "PHP", "hack" => "Hack"}; }
  92. S E T S

  93. S E T S • Unordered Collection with no keys

    • Unique values <?hh // strict function getTags(): Set<string> { return Set { "php", "hack", "hhvm" }; }
  94. I M M U TA B I L I T

    Y
  95. I M M U TA B I L I T

    Y • Simply prefix the class name with Imm <?hh // strict function getTags(): ImmMap<string, string> { $map = ImmMap {"php" => "PHP", "hack" => "Hack"}; $map["hhvm"] = "HHVM"; } <file>:4:5,16: You cannot mutate this (Typing[4011]) <file>:3:12,52: This is an object of type ImmMap
  96. A P P E N D I N G E

    L E M E N T S
  97. A P P E N D I N G E

    L E M E N T S • Only Vectors allow $foo[] = "bar" syntax • Pairs and Tuples do not allow additional elements • Maps allow appending of Pairs. First value is key, second value is value <?hh // strict function appendToMap(): void { $m = Map { 0 => "foo", 1 => "bar" }; $m[] = Pair { 5, "bat" }; var_dump($m); }
  98. O B J E C T O R I E

    N T E D I N T E R FA C E
  99. O B J E C T O R I E

    N T E D I N T E R FA C E • Based on the SPL • Allows adding/removing/manipulation methods • Allows you to use them as objects, rather than just “better” arrays
  100. B R E A K I N G I T

    E R AT I O N
  101. B R E A K I N G I T

    E R AT I O N • Because collections are objects, you cannot modify them during iteration. Fatal error: Uncaught exception 'InvalidOperationException' with message 'Collection was modified during iteration' in <file>
  102. S H A P E S

  103. S H A P E S • Tuples with a

    pre-defined structure • Can be used as a form of validation • Ensures integrity
  104. S H A P E S newtype HTTPRequest = shape(

    'status' => HTTPStatusCode, 'headers' => Map<string, string>, 'data' => shape ( 'GET' => ?Map<string, mixed>, 'POST' => ?Map<string, mixed>, 'COOKIE' => ?Map<string, mixed>, 'SERVER' => Map<string, mixed> ), 'body' => ?string );
  105. A N O N Y M O U S F

    U N C T I O N S
  106. A N O N Y M O U S F

    U N C T I O N S • Like closures but they inherit parent scope • Much more concise
 
 
 • Multiple Expressions $fn = $args ==> expression; $fn = ($arg1, $arg2) ==> { 
 expression; return ...; 
 };
  107. A N O N Y M O U S F

    U N C T I O N S $list = <ul/>; array_walk($items, $item ==> $list->appendChild(
 <li>{$item}</li> )); echo $list;
  108. X H P — X M L F R A

    G M E N T S A S E X P R E S S I O N S
  109. X H P — X M L F R A

    G M E N T S A S E X P R E S S I O N • Makes XML syntax a top-level syntax • No longer strings • Auto-escapes for security • OO semantics allow composable widgets for templates • Must be XML, e.g. <br /> echo <p>Hello World</p>;
  110. X H P ( C O N T. ) •

    Classes are defined by prefixing the class name with a colon • Classes extend the :x:element base class which has DOM-like methods: • appendChild() • prependChild() • replaceChildren() • getChildren() • getFirstChild() • getLastChild() • getAttribute(), getAttributes() • setAttribute(), setAttributes() • isAttributeSet() • removeAttribute()
  111. T H E P H P WAY <?php echo '<ul>';

    foreach ($items as $item) { echo '<li>'; echo htmlentities($item, ENT_QUOTES, 'UTF-8'); echo '</li>'; } echo '</ul>'; ?>
  112. T H E H A C K WAY <?hh $list

    = <ul/>; foreach ($items as $item) { $list->appendChild(<li>{$item}</li>); }
  113. X H P - B O O T S T

    R A P print <bootstrap:dropdown> <bootstrap:button> Dropdown <bootstrap:caret /> </bootstrap:button> <bootstrap:dropdown:menu> <bootstrap:dropdown:item href="#"> Foo </bootstrap:dropdown:item> <bootstrap:dropdown:item href="#"> Bar </bootstrap:dropdown:item> <bootstrap:dropdown:item disabled="true" href="#"> Disabled </bootstrap:dropdown:item> </bootstrap:dropdown:menu> </bootstrap:dropdown>;
  114. X H P - B O O T S T

    R A P print <bootstrap:dropdown> <bootstrap:button> Dropdown <bootstrap:caret /> </bootstrap:button> <bootstrap:dropdown:menu> <bootstrap:dropdown:item href="#"> Foo </bootstrap:dropdown:item> <bootstrap:dropdown:item href="#"> Bar </bootstrap:dropdown:item> <bootstrap:dropdown:item disabled="true" href="#"> Disabled </bootstrap:dropdown:item> </bootstrap:dropdown:menu> </bootstrap:dropdown>;
  115. X H P - B O O T S T

    R A P • Validation — invalid tags are errors, attribute values (e.g. class names) are validated • Semantic Markup — tags are more descriptive • Encapsulation — create re-usable complex components • Future Proof — Change the underlying implementation
  116. O T H E R F E AT U R

    E S
  117. O T H E R F E AT U R

    E S • Async MySQL, memcached and Curl (3.6+) • Async functions • Annotations • enums • Generics • more…
  118. C O M M A N D L I N

    E T O O L S
  119. S TAT I C A N A LY S I

    S
  120. H H _ S E R V E R

  121. S TAT I C A N A LY S I

    S : H H _ S E R V E R • Monitors files for changes using inotify • Validates the files using static analysis • Analyses .php and .hh files with the <?hh open tag
  122. H H _ C L I E N T

  123. S TAT I C A N A LY S I

    S • Starts hh_server if necessary • Retrieves the results of the static analysis from hh_server • Displays them in the CLI • Integrates with editors/IDEs • Each line shows:
 file:line:start column,end column: Error
  124. 1. <?hh // strict 2. function greeting(string $who):void { 3.

    return "Hello $who"; 4. } 5. 6. function sayHello() { 7. echo greeting(1.0); 8. } file:3:5,10: You cannot return a value (Typing[4084]) file:2:32,35: This is a void function file:6:10,17: Was expecting a return type hint (Typing[4030]) file:7:19,21: Invalid argument (Typing[4110]) file:2:19,24: This is a string file:7:19,21: It is incompatible with a float
  125. S TAT I C A N A LY S I

    S
  126. V I M I N T E G R AT

    I O N
  127. R E FA C T O R I N G

  128. R E FA C T O R I N G

    • hh_client check --refactor • Can rename classes, functions, or methods • Does not yet support namespaces :( • Static analysis makes these changes much less prone to error
  129. R E FA C T O R I N G

    $ hh_client check --refactor WARNING: This tool will only refactor references in typed, hack code. Its results should be manually verified. Namespaces are not yet supported. What would you like to refactor: 1 - Class 2 - Function 3 - Method Enter 1, 2, or 3: 2 Enter function name: sayHello Enter a new name for this function: sayHi Rewrote 2 files.
  130. H A C K I F I C AT I

    N G
  131. C O N V E R T I N G

    F R O M P H P T O H A C K
  132. H A C K I F I C AT I

    N G • Change the opening tag • Add soft type hints • Run the code with soft type hints and gather the logs • Feed the logs back into HHVM to remove invalid hints (ensures there is no invalid inference) • Harden the type hints
  133. H A C K I F I C AT O

    R • Hackificator changes the opening tag from PHP to Hack: • $ hackificator .
  134. A D D S O F T T Y P

    E H I N T S • Uses static analysis to figure out and add soft type hints: • $ hh_server --convert ./ ./ public static function makeBarcode(
 $barcode, 
 $barcodeConfig = array()
 ) public static function makeBarcode(
 @?string $barcode, 
 @array $barcodeConfig = array()
 ) : @\Zend\Barcode\Object\ObjectInterface\
  135. V E R I F Y S O F T

    T Y P E H I N T S • Run the code (tests suites are great for this!) • Capture the hhvm log file
  136. R E M O V E I N VA L

    I D S O F T T Y P E H I N T S • Feed the log file into hack_remove_soft_types to remove invalid types: • $ hack_remove_soft_types --delete-from-log hhvm.log • Note: This tool is dumb and requires paths to be identical
  137. H A R D E N S O F T

    T Y P E S • Finally: Harden all the left over types $ hack_remove_soft_types --harden FILE
 
 • Run it over all files in a directory: $ find ./ -type f -name '*.php' -exec hack_remove_soft_types --harden '{}' ';'
  138. R E S O U R C E S •

    Hack Lang: http://hacklang.org • HHVM: http://hhvm.com • Building a Better PHP: https://blog.engineyard.com/2014/hhvm-hack (Engine Yard Blog)
  139. F E E D B A C K & Q

    U E S T I O N S Feedback: Twitter: Email: Slides: https://joind.in/ @dshafik me@daveyshafik.com http://daveyshafik.com/slides 14866