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

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

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

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

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

Avatar for yuuki takezawa

yuuki takezawa

March 06, 2018
Tweet

More Decks by yuuki takezawa

Other Decks in Technology

Transcript

  1. 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
  2. <?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 ࢀর
  3. <?hh Type Check: Partial • PHPͷܕએݴ strictͱಉఔ౓ • ඞཁҎ্ʹܕνΣοΫ͸͠ͳ͍ •

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

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

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

    __invokeͷѻ͍͕PHPͱҟͳΔͨΊܕΤϥʔൃੜ
 => strictͰ__invokeͰॲཧ͢Δϛυϧ΢ΣΞ(ඇPSR-15)
 ΛऔΓೖΕΔ৔߹͸஫ҙ
 • HackͰ࣮૷͢ΔͷͰ͋Ε͹StrictΛج४ʹɺ
 Ұ෦͚ͩPartial͕ϕετ
  7. class Example { public function foo<T>(T $t): T { return

    $t; } } inst_meth(new Example(), ‘foo’); Must be a constant string.
  8. <?hh // strict • TypeScript, flowͱಉ༷ͳܕఆٛϑΝΠϧ • ୯ମͰ͸࣮ߦͰ͖ͳ͍ • ܕఆٛϑΝΠϧΛ࡞Δ͜ͱͰɺܕએݴΛߦ͍ɺ


    ݫ֨ϞʔυͰ࣮ߦ͢Δ͜ͱ͕Մೳ
 • ࢖͍͍ͨPHPͷίʔυ΋ิ׬ͯ͠΄͍͠ʂʂ
 ͱ͍͏ํ͸࡞੒ͯ͠packagistͰެ։͍ͯͩ͘͠͞ʂ
  9. 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 ʹ΋ؚ·Ε͍ͯ·͢
  10. 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.

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

    get(string $id): mixed; public function has(string $id): bool; } ໭ΓͷܕએݴΛ௥Ճ͚ͨͩ͠
  12. <?hh .hhconfig • HackͰ࣮ߦ؀ڥʹઃஔ͢ΔϑΝΠϧ • ࣮͸༷ʑͳઃఆΛهड़Ͱ͖Δ • PHPར༻Λ૝ఆ͠ͳ͍(PHPࠞࡏෆՄ) 
 assume_php

    = false(default: true) • Type Checker Ұ෦ແࢹ
 ignored_paths = [ "vendor/hhvm/hhast/.+" ]
  13. <?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Λઃஔ
  14. <?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

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

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

    (Generator<Tk, +Tv, -Ts>)
 • fast-routeͷhhi͕ϝϯς͞Ε͍ͯͳ͍ͨΊType Error
 PHPͱͷ෼཭͕ਐΉͨΊPRͤͣ
 • ಈ͔ͳ͍෦෼Λ
 Hack࣮૷ͷϥΠϒϥϦͱೖΕସ͑Δ͜ͱʹ
  17. 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Λฦ٫
  18. 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
  19. 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࣮૷
  20. 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෩ Πϯελϯεੜ੒࣌ʹ࣮ߦ͢Δ
  21. <?hh // strict namespace Psr\Container; interface ContainerInterface { public function

    get(string $id): mixed; public function has(string $id): bool; } PSR-11 get (): mixed
  22. $logger = $container->get(‘LoggerInterface'); invariant( $logger instanceof LoggerInterface, "Interface '\Psr\Log\LoggerInterface' is

    not implemented by this class", ); invariant͸HackͰ༻ҙ͞Ε͍ͯΔؔ਺ ୈҰҾ਺ͷ৚͕݅falseͷ৔߹ʹType Error
  23. 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; } }
  24. 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Λݕࠪ
  25. \Nazg\Http\HttpMethod::GET => ImmMap { '/' => ImmVector {App\Action\IndexAction::class}, }, GETʹରԠͤ͞ΔRouteΛهड़

    ‘/’ ʹΞΫηε࣌ʹىಈ͢Δϛυϧ΢ΣΞΛࢦఆ ImmVector<classname<\Psr\Http\Server\MiddlewareInterface>> هड़ͨ͠௨Γʹ࣮ߦ͞ΕΔ
 ActionΫϥε΋ಉΠϯλʔϑΣʔε࣮૷ͷͨΊ۠ผͳ͠
  26. 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
  27. 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 ૢ࡞ ࢦఆͨ͠ΠϯσοΫεͷΞΠςϜΛ࡟আ
  28. <?hh খ͞ͳϥΠϒϥϦͷू߹ମ΁ • ࠷ۙͷPHPͱಉ༷ʹૄ݁߹ʹ • PSR-11, PSR-7, PSR-15Λར༻ͨͨ͠Ίɺ
 PHPͷϥΠϒϥϦ΋HackͰ࣮ߦՄೳͳ΋ͷ͸OK •

    router͸hack-routerҎ্ͷ΋ͷ͕ͳ͍ͨΊɺ
 hack-routerʹͷΈґଘ • PSR-7͸zend-diactorosΛσϑΥϧτʹ
 (Symfony Component 4Ҏ߱͸Hack ͸αϙʔτ͞Εͳ͍)