Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hackで作るマイクロフレームワーク

 Hackで作るマイクロフレームワーク

For PHPerKaigi 2018
PHPからHackへ移行するときに気をつけるもの
PHPのライブラリを使ってフレームワークなどを作るにはどうしたらいいか、という内容です

yuuki takezawa

March 06, 2018
Tweet

More Decks by yuuki takezawa

Other Decks in Technology

Transcript

  1. HackͰ࡞Δ ϚΠΫϩϑϨʔϜϫʔΫ yuuki takezawa (ytake) PHPerKaigi 2018

  2. Profile • ஛ᖒ ༗و / ytake • גࣜձࣾΞΠελΠϧ • PHP,

    Hack, Go, Scala • Apache Hadoop, Apache Spark, Apache Kafka
 • twitter https://twitter.com/ex_takezawa • facebook https://www.facebook.com/yuuki.takezawa • github https://github.com/ytake
  3. author

  4. None
  5. None
  6. ͸ͳ͢͜ͱ • ։ൃ͢ΔͨΊͷ஌ࣝ 
 .hhconfigɺdecl / strictɺhhi • ڧྗͳϥΠϒϥϦ
 hhvm/hhvm-autoloadɺhhvm/hack-router

    • PHPϥΠϒϥϦͱͷޓ׵Λอͭ։ൃ
  7. <?hh ։ൃ͢ΔͨΊͷ஌ࣝ

  8. <?hh Hackʁ • PHPͷίʔυ͸جຊతʹͦͷ··࣮ߦՄೳ • ݫ֨ͳType Checker • AsyncɺXHPɺGenericsɺCollections •

    3.24 LTS (2018/03/10 ݱࡏ) • HHVM͸ɺPHPͱHack྆ํΛαϙʔτ͢ΔͷͰ͸ͳ ͘ɺHackΛର৅ͱ͍ͯ͘͠ *Forget PHP! Facebook’s HHVM engine switches to Hack instead https://www.infoworld.com/article/3226489/web-development/forget-php-facebooks-hhvm-engine-switches-to-hack-instead.html ࢀর
  9. <?hh Type checker • Ϟʔυ͸3ͭ Partial / Strict / Decl

    
 • σϑΥϧτͰ͸Partial
  10. <?hh Type Check: Partial • PHPͷܕએݴ strictͱಉఔ౓ • ඞཁҎ্ʹܕνΣοΫ͸͠ͳ͍ •

    ओʹPHPͷίʔυΛ Hackͱ࣮ͯ͠ߦ͢Δ
 ίʔυҠ২தͷϑΝΠϧ౳Ͱར༻ • ࢀর౉͠ ར༻Մೳ
  11. <?hh Type Check: Decl • <?hh // decl • ܕνΣοΫ͸͠ͳ͍

    • ଞͷίʔυνΣοΫ࣌ʹ͸ࢀর͞ΕΔ • New Hack code should never be written 
 in decl mode
  12. <?hh Type Check: Strict • <?hh // strict • PHPґଘ͕ͳ͘ɺ100%

    HackͰ࣮૷͢Δ৔߹ʹબ୒ • ݫ֨ͳܕνΣοΫΛߦ͏ϞʔυͷͨΊɺ
 ίʔυϨϏϡʔ࣌ͷܕએݴʹ͍ͭͯͷٞ࿦͸ͳ͠ • ૝ఆ֎ͷܕม׵ͳͲ͸ߦΘΕͳ͍ͨΊɺ
 ϨϏϡʔ͸ΫϥεઃܭɾΞʔΩςΫνϟͳͲʹ
 ϑΥʔΧεͰ͖Δ • Type Checkerʹڭ͑ΔͨΊͷίʔυʹ
  13. <?hh Type Check: Strict • ࠷΋ݫ֨ͳϞʔυͷͨΊɺ
 PHPϥΠϒϥϦΛ࢖͏ίʔυͰ͸ܕએݴΤϥʔൃੜ
 => hhiϑΝΠϧΛ࡞੒(ޙड़) •

    __invokeͷѻ͍͕PHPͱҟͳΔͨΊܕΤϥʔൃੜ
 => strictͰ__invokeͰॲཧ͢Δϛυϧ΢ΣΞ(ඇPSR-15)
 ΛऔΓೖΕΔ৔߹͸஫ҙ
 • HackͰ࣮૷͢ΔͷͰ͋Ε͹StrictΛج४ʹɺ
 Ұ෦͚ͩPartial͕ϕετ
  14. <?hh callableʹؾΛ͚ͭΔ • PHPͰ͸ callableͰܕએݴ͞Ε͍ͯΔ΋ͷ͸Closureɺ
 ·ͨ͸__invokeϝιουΛ࣋ͭΫϥεͰ͋Ε͹ྑ͍
 • HackͰ͸callableͰ͸ͳ͘ɺແ໊ؔ਺ͷҾ਺΋ܕએݴ
 __invoke͸callableѻ͍Ͱ͸ͳ͍
 ->

    PHPͷ __invoke Λ࣮૷͢Δϛυϧ΢ΣΞܥΛ
 ͦͷ··strictͰҠ২͸Ͱ͖ͳ͍
 • inst_meth ͱ͍͏બ୒ࢶ
  15. class Example { public function foo<T>(T $t): T { return

    $t; } } inst_meth(new Example(), ‘foo’); Must be a constant string.
  16. <?hh hhi

  17. <?hh // strict • TypeScript, flowͱಉ༷ͳܕఆٛϑΝΠϧ • ୯ମͰ͸࣮ߦͰ͖ͳ͍ • ܕఆٛϑΝΠϧΛ࡞Δ͜ͱͰɺܕએݴΛߦ͍ɺ


    ݫ֨ϞʔυͰ࣮ߦ͢Δ͜ͱ͕Մೳ
 • ࢖͍͍ͨPHPͷίʔυ΋ิ׬ͯ͠΄͍͠ʂʂ
 ͱ͍͏ํ͸࡞੒ͯ͠packagistͰެ։͍ͯͩ͘͠͞ʂ
  18. packagistʹ͋ΔhhiϑΝΠϧͨͪ • hack-psr/psr7-http-message-hhi • 91carriage/phpunit-hhi • ytake/psr-http-handlers-hhi • ytake/psr-container-hhi •

    libreworks/psr3-log-hhi
 • HackରԠϥΠϒϥϦ։ൃऀ͕࢖͏΋ͷఔ౓͔͋͠Γ· ͤΜ • nikic/fast-route ʹ΋ؚ·Ε͍ͯ·͢
  19. PSR For Hack • https://github.com/facebookexperimental/hack-http- request-response-interfaces • This project aims

    to create standard request and response interfaces for Hack, using PSR-7 as a starting point.

  20. <?hh hhi PSR-11 Container Interface : Example

  21. <?hh // strict namespace Psr\Container; interface ContainerInterface { public function

    get(string $id): mixed; public function has(string $id): bool; } ໭ΓͷܕએݴΛ௥Ճ͚ͨͩ͠
  22. <?hh .hhconfig

  23. <?hh .hhconfig • HackͰ࣮ߦ؀ڥʹઃஔ͢ΔϑΝΠϧ • ࣮͸༷ʑͳઃఆΛهड़Ͱ͖Δ • PHPར༻Λ૝ఆ͠ͳ͍(PHPࠞࡏෆՄ) 
 assume_php

    = false(default: true) • Type Checker Ұ෦ແࢹ
 ignored_paths = [ "vendor/hhvm/hhast/.+" ]
  24. <?hh ڧྗͳϥΠϒϥϦ

  25. <?hh hhvm/hhvm-autoload

  26. <?hh HHVM-Autoload • HHVM؀ڥઐ༻ͷComposer Plugin
 A Composer plugin for autoloading

    classes, enums, functions, typedefs, and constants on HHVM. • vendor/autoload.php Λ vendor/hh_autoload.php ʹ • hh_autoload.jsonΛઃஔ
  27. <?hh HHVM-Autoload • લड़ͨ͠hhiϑΝΠϧͳͲΛvendorσΟϨΫτϦ͔Β
 ݟ͚ͭग़͠ɺTypeCheckerରԠ͚ͩͰ͸ͳ͘ɺ
 Nuclide, VSCͷิ׬ͱͯ͠΋࡞༻
 • HackͰ࣮૷͢Δ৔߹͸ඞͣೖΕ͓͖ͯ·͠ΐ͏

  28. <?hh Composer install • php7ґଘͷ΋ͷ͸iniϑΝΠϧ͔ίϚϯυͰղܾ
 hhvm -d xdebug.enable=0 -d hhvm.jit=0

    -d hhvm.php7.all=1\ 
 -d hhvm.hack.lang.auto_typecheck=0 \
 $(which composer) require vendor/package

  29. Zend Expressive with HHVM/Hack

  30. <?hh Zend Expressive 1.0 • HHVM/HackͰ΋ɺԿ΋ؾʹͤͣར༻Մೳ • Zend ServiceManager +

    HHVM/HackͰ࣮૷ɾՔಇ
 • PHPײ֮ͰखܰʹHHVM/HackΞϓϦέʔγϣϯΛ։ൃ ͢Δ৔߹ʹΦεεϝ
  31. <?hh Zend Expressive >= 2.0 • zend-config-aggregator ͷҰ෦ͷίʔυ͕ಈ࡞ͤͣ
 __invoke, yield͕ಈ͔ͣ

    (Generator<Tk, +Tv, -Ts>)
 • fast-routeͷhhi͕ϝϯς͞Ε͍ͯͳ͍ͨΊType Error
 PHPͱͷ෼཭͕ਐΉͨΊPRͤͣ
 • ಈ͔ͳ͍෦෼Λ
 Hack࣮૷ͷϥΠϒϥϦͱೖΕସ͑Δ͜ͱʹ
  32. <?hh • PHPϥΠϒϥϦͷ࣮૷Λؾʹ͢Δͷ͸ɾɾɾ
 • PHPฒΈʹίʔυิ׬͕Ͱ͖ɺ
 HackͳΒͰ͸ͷ࣮૷Λ͢Δʹ͸HackͰ࡞Δ͔͠ͳ͍

  33. <?hh hhvm/hack-router

  34. <?hh hack-router • PSR-7ରԠͷHackઐ༻RouterϥΠϒϥϦ
 • Ҏલ͸fast-route֦ு͕ͩͬͨআ֎
 • GenericsΛ࢖͍ɺ
 Routerͷ࣮૷Λ஌Βͣʹར༻Ͱ͖ΔϥΠϒϥϦ
 •

    PHPαϙʔτΛ΍ΊΔ͜ͱʹ͋ͨΓɺ
 ͜ͷϥΠϒϥϦϕʔεʹҠߦ
  35. type TResponder = ImmVector<classname<MiddlewareInterface>>; final class Router extends BaseRouter<TResponder> {

    
 <<__Override>> protected function getRoutes( ): ImmMap<HackRouterHttpMethod, ImmMap<string, TResponder>> { $i = $this->routeMap->getIterator(); $map = []; while ($i->valid()) { $map[$this->convertHttpMethod($i->key())] = $i->current(); $i->next(); } return new ImmMap($map); } ࢦఆͨ͠ΫϥεɺIFΛܧঝɺ࣮૷ͨ͠Ϋϥε໊એݴ Routerʹ֨ೲ͠ɺϚον࣌ʹTResponderΛฦ٫
  36. <?hh ytake/hh-container

  37. <?hh PSR-11 • PHPϥΠϒϥϦΛ࢖͍ଓ͚Δҙຯ΋͋·Γͳ͍ͨΊɺ
 Hackઐ༻ͷPimpleϥΠΫͳίϯςφΛ։ൃ
 • PSR-11 hhiΛ༻ҙ͠ɺHackͰ࣮૷
 • HackͳΒͰ͸ͷํ๏ͰSingleton͸

    <<__Memoize>>
 • Πϯελϯεղܾํ๏ఆٛ͸ࣗ༝
  38. public function set( string $id, (function(FactoryContainer): mixed) $callback, Scope $scope

    = Scope::PROTOTYPE, ): void { if (!$this->locked) { $this->bindings->add(Pair {$id, $callback}); $this->scopes->add(Pair {$id, $scope}); } } Closure Type enums Map
  39. use Ytake\HHContainer\FactoryContainer; $container = new FactoryContainer(); $container->set( MessageClass::class, $container ==>

    new MessageClass(‘testing') );
 $container->parameters( MessageClient::class, 'message', $container ==> $container->get(‘message.class’) ); $instance = $container->get(MessageClient::class); Lambda PSR-11࣮૷
  40. final class TestingInvokable { public function __invoke(FactoryContainer $container): int {

    return 1; } } $container = new \Ytake\HHContainer\FactoryContainer(); $container->set(TestingInvokable::class, $container ==> $container->callable( new \Ytake\HHContainer\Invokable( new TestingInvokable(), '__invoke', $container ) ) ); Zend ServiceManager Factory෩ Πϯελϯεੜ੒࣌ʹ࣮ߦ͢Δ
  41. <?hh PSR-11 • Zend ServiceManagerޓ׵Λ໨ࢦͨ͠ϥΠτ൛
 • Zend ExpressiveରԠͨ͠ͱ͜Ζɺ
 ಈతϝιουίʔϧ͸ਪ঑͞Ε͍ͯͳ͍ͨΊɺ
 Type

    Error • /* UNSAFE_EXPR */
  42. ໽հͳmixed PSR-11ͷྫ(strict)

  43. <?hh // strict namespace Psr\Container; interface ContainerInterface { public function

    get(string $id): mixed; public function has(string $id): bool; } PSR-11 get (): mixed
  44. $container = require 'config/services.php'; $app = $container->get('Zend\Expressive\Application'); mixedͳͨΊɺԿ͕ฦ٫͞ΕΔ͔Θ͔Βͳ͍

  45. $config = $container->get('config_array'); if (is_array($config)) { if (array_key_exists('config_array', $config)) {

    return $config['config_array']; } } arrayͰ͋Δ͜ͱΛࣔ͢
  46. $logger = $container->get(‘LoggerInterface'); invariant( $logger instanceof LoggerInterface, "Interface '\Psr\Log\LoggerInterface' is

    not implemented by this class", ); invariant͸HackͰ༻ҙ͞Ε͍ͯΔؔ਺ ୈҰҾ਺ͷ৚͕݅falseͷ৔߹ʹType Error
  47. class TypeAssert { const type Tk = LoggerInterface; public static

    function assert<Tk>(Tk $t): this::Tk { invariant( $logger instanceof LoggerInterface, "Interface '\Psr\Log\LoggerInterface' is not implemented by this class", ); return $t; } }
  48. <?hh mixed • ༷ʑͳ஋͕ฦ٫͞ΕΔͨΊɺmixed͸࢖͏΂͖Ͱ͸ͳ͍
 • PSRʹͩ͜ΘΔඞཁ΋ͳ͍
 (४ڌ͠ͳ͍͜ͱΛݕ౼த)
 • େن໛ͳ։ൃͳͲͰ͸ͳΔ΂͘Strict(ฐࣾஊ)


  49. Forget PHP! Facebook’s HHVM engine switches to Hack instead

  50. <?hh hhvm/type-assert

  51. use namespace Facebook\TypeAssert; class Foo { const type TAPIResponse =

    shape( 'id' => int, 'user' => string, 'data' => shape( /* ... */ ), ); public static function getAPIResponse(): self::TAPIResponse { $json_string = file_get_contents('https://api.example.com'); $array = json_decode($json_string, true); return TypeAssert\matches_type_structure( type_structure(self::class, 'TAPIResponse'), $array, ); } } ShapeͰϑΟʔϧυࢦఆ ͍͋·͍ͳܕฦ٫APIΛݕࠪ
  52. RouterͱContainerɺ ؆୯ͳValidation͕͋Ε͹ ࠷௿ݶͷಈ࡞͕Մೳʹ

  53. Dependency / Container Build Application Routing/Dispatcher Middleware

  54. type TResponder = ImmVector<classname<\Psr\Http\Server\MiddlewareInterface>>; hack routerͷTResponder ϦΫΤετʹରԠ͢Δ΋ͷ͕ొ࿥͞ΕͯೖΕ͹ɺ TResponderͰࢦఆͨ͠ܕΛฦ٫͢Δ

  55. \Nazg\Http\HttpMethod::GET => ImmMap { '/' => ImmVector {App\Action\IndexAction::class}, }, GETʹରԠͤ͞ΔRouteΛهड़

    ‘/’ ʹΞΫηε࣌ʹىಈ͢Δϛυϧ΢ΣΞΛࢦఆ ImmVector<classname<\Psr\Http\Server\MiddlewareInterface>> هड़ͨ͠௨Γʹ࣮ߦ͞ΕΔ
 ActionΫϥε΋ಉΠϯλʔϑΣʔε࣮૷ͷͨΊ۠ผͳ͠
  56. public function run(ServerRequestInterface $serverRequest): void { $container = $this->getContainer(); $this->bootstrap($container);

    $router = $container->get(BaseRouter::class); invariant( $router instanceof BaseRouter, "%s class must extend %s", get_class($router), BaseRouter::class, ); list($middleware, $attributes) = $router->routePsr7Request($serverRequest); hack-routerܧঝΫϥεΛऔΓग़͠ from PSR-7
  57. public function __construct( Traversable<classname<MiddlewareInterface>> $queue, ?Resolvable $resolver = null, )

    { $this->queue = new Vector($queue); $this->resolver = (is_null($resolver)) ? new InstanceResolver() : $resolver; } public function shift(): MiddlewareInterface { $this->queue->reverse(); $current = $this->queue->pop(); return $this->resolver->resolve($current); } public function cancel(int $index): Vector<classname<MiddlewareInterface>> { return $this->queue->removeKey($index); } Collection (Vector) Vector ૢ࡞ ҰͭͣͭऔΓग़࣮ͯ͠ߦ Vector ૢ࡞ ࢦఆͨ͠ΠϯσοΫεͷΞΠςϜΛ࡟আ
  58. https://github.com/ytake/nazg-skeleton https://github.com/nazg-hack/framework

  59. <?hh খ͞ͳϥΠϒϥϦͷू߹ମ΁ • ࠷ۙͷPHPͱಉ༷ʹૄ݁߹ʹ • PSR-11, PSR-7, PSR-15Λར༻ͨͨ͠Ίɺ
 PHPͷϥΠϒϥϦ΋HackͰ࣮ߦՄೳͳ΋ͷ͸OK •

    router͸hack-routerҎ্ͷ΋ͷ͕ͳ͍ͨΊɺ
 hack-routerʹͷΈґଘ • PSR-7͸zend-diactorosΛσϑΥϧτʹ
 (Symfony Component 4Ҏ߱͸Hack ͸αϙʔτ͞Εͳ͍)
  60. <?hh খ͞ͳϥΠϒϥϦͷू߹ମ • Request BodyͳͲͷValidationʹtype-assert
 • mixedΛέΞ͢Δ


  61. <?hh • Hack͔ͩΒԿ͔ಛผͳ΋ͷɺͱ͍͏ͷ͸ແ͍
 *ϥϯλΠϜӠʑ͸আ͘
 • όά͕গͳ͍ݎ࣮ͳΞϓϦέʔγϣϯ΁
 • ίʔυϨϏϡʔෛՙܰݮ • ΈΜͳ͕ࢥ͏΄Ͳ೉͘͠ͳ͍ʂ


    • PHPؾ෼Ͱ͔ΜͨΜͳϚΠΫϩϑϨʔϜϫʔΫ։ൃ
  62. <?hh ͓·͚ • GoͰ΍Βͳ͔ͬͨͷ͔ʁ
 -> Go(Goa, Echo)͸ଟ͘ͷAPIͰಋೖࡁΈ
 େ͖͘ݴޠΛม͑ͣʹɺ
 PHPͷ։ൃऀ͕࢖͑Δ΋ͷΛ૿΍͔ͨͬͨ͠
 •

    segmentation faultͭΒ͘ͳ͍Ͱ͔͢ʁ
 hhvm-dbg + straceͰͳΜͱ͔ͳΓ·͢