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

    View full-size slide

  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

    View full-size slide

  3. ͸ͳ͢͜ͱ
    • ։ൃ͢ΔͨΊͷ஌ࣝ 

    .hhconfigɺdecl / strictɺhhi
    • ڧྗͳϥΠϒϥϦ

    hhvm/hhvm-autoloadɺhhvm/hack-router
    • PHPϥΠϒϥϦͱͷޓ׵Λอͭ։ൃ

    View full-size slide

  4. ։ൃ͢ΔͨΊͷ஌ࣝ

    View full-size slide

  5. • 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 ࢀর

    View full-size slide

  6. • Ϟʔυ͸3ͭ Partial / Strict / Decl 

    • σϑΥϧτͰ͸Partial

    View full-size slide

  7. • PHPͷܕએݴ strictͱಉఔ౓
    • ඞཁҎ্ʹܕνΣοΫ͸͠ͳ͍
    • ओʹPHPͷίʔυΛ Hackͱ࣮ͯ͠ߦ͢Δ

    ίʔυҠ২தͷϑΝΠϧ౳Ͱར༻
    • ࢀর౉͠ ར༻Մೳ

    View full-size slide

  8. • • ܕνΣοΫ͸͠ͳ͍
    • ଞͷίʔυνΣοΫ࣌ʹ͸ࢀর͞ΕΔ
    • New Hack code should never be written 

    in decl mode

    View full-size slide

  9. • • PHPґଘ͕ͳ͘ɺ100% HackͰ࣮૷͢Δ৔߹ʹબ୒
    • ݫ֨ͳܕνΣοΫΛߦ͏ϞʔυͷͨΊɺ

    ίʔυϨϏϡʔ࣌ͷܕએݴʹ͍ͭͯͷٞ࿦͸ͳ͠
    • ૝ఆ֎ͷܕม׵ͳͲ͸ߦΘΕͳ͍ͨΊɺ

    ϨϏϡʔ͸ΫϥεઃܭɾΞʔΩςΫνϟͳͲʹ

    ϑΥʔΧεͰ͖Δ
    • Type Checkerʹڭ͑ΔͨΊͷίʔυʹ

    View full-size slide

  10. • ࠷΋ݫ֨ͳϞʔυͷͨΊɺ

    PHPϥΠϒϥϦΛ࢖͏ίʔυͰ͸ܕએݴΤϥʔൃੜ

    => hhiϑΝΠϧΛ࡞੒(ޙड़)
    • __invokeͷѻ͍͕PHPͱҟͳΔͨΊܕΤϥʔൃੜ

    => strictͰ__invokeͰॲཧ͢Δϛυϧ΢ΣΞ(ඇPSR-15)

    ΛऔΓೖΕΔ৔߹͸஫ҙ

    • HackͰ࣮૷͢ΔͷͰ͋Ε͹StrictΛج४ʹɺ

    Ұ෦͚ͩPartial͕ϕετ

    View full-size slide

  11. • PHPͰ͸ callableͰܕએݴ͞Ε͍ͯΔ΋ͷ͸Closureɺ

    ·ͨ͸__invokeϝιουΛ࣋ͭΫϥεͰ͋Ε͹ྑ͍

    • HackͰ͸callableͰ͸ͳ͘ɺແ໊ؔ਺ͷҾ਺΋ܕએݴ

    __invoke͸callableѻ͍Ͱ͸ͳ͍

    -> PHPͷ __invoke Λ࣮૷͢Δϛυϧ΢ΣΞܥΛ

    ͦͷ··strictͰҠ২͸Ͱ͖ͳ͍

    • inst_meth ͱ͍͏બ୒ࢶ

    View full-size slide

  12. class Example {
    public function foo(T $t): T {
    return $t;
    }
    }
    inst_meth(new Example(), ‘foo’);
    Must be a constant string.

    View full-size slide

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

    ݫ֨ϞʔυͰ࣮ߦ͢Δ͜ͱ͕Մೳ

    • ࢖͍͍ͨPHPͷίʔυ΋ิ׬ͯ͠΄͍͠ʂʂ

    ͱ͍͏ํ͸࡞੒ͯ͠packagistͰެ։͍ͯͩ͘͠͞ʂ

    View full-size slide

  14. 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 ʹ΋ؚ·Ε͍ͯ·͢

    View full-size slide

  15. 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.


    View full-size slide

  16. hhi
    PSR-11 Container Interface : Example

    View full-size slide

  17. namespace Psr\Container;
    interface ContainerInterface
    {
    public function get(string $id): mixed;
    public function has(string $id): bool;
    }
    ໭ΓͷܕએݴΛ௥Ճ͚ͨͩ͠

    View full-size slide

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

    assume_php = false(default: true)
    • Type Checker Ұ෦ແࢹ

    ignored_paths = [ "vendor/hhvm/hhast/.+" ]

    View full-size slide

  19. ڧྗͳϥΠϒϥϦ

    View full-size slide

  20. hhvm/hhvm-autoload

    View full-size slide

  21. • 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Λઃஔ

    View full-size slide

  22. • લड़ͨ͠hhiϑΝΠϧͳͲΛvendorσΟϨΫτϦ͔Β

    ݟ͚ͭग़͠ɺTypeCheckerରԠ͚ͩͰ͸ͳ͘ɺ

    Nuclide, VSCͷิ׬ͱͯ͠΋࡞༻

    • HackͰ࣮૷͢Δ৔߹͸ඞͣೖΕ͓͖ͯ·͠ΐ͏

    View full-size slide

  23. • 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


    View full-size slide

  24. Zend Expressive with HHVM/Hack

    View full-size slide

  25. • HHVM/HackͰ΋ɺԿ΋ؾʹͤͣར༻Մೳ
    • Zend ServiceManager + HHVM/HackͰ࣮૷ɾՔಇ

    • PHPײ֮ͰखܰʹHHVM/HackΞϓϦέʔγϣϯΛ։ൃ
    ͢Δ৔߹ʹΦεεϝ

    View full-size slide

  26. = 2.0
    • zend-config-aggregator ͷҰ෦ͷίʔυ͕ಈ࡞ͤͣ

    __invoke, yield͕ಈ͔ͣ (Generator)

    • fast-routeͷhhi͕ϝϯς͞Ε͍ͯͳ͍ͨΊType Error

    PHPͱͷ෼཭͕ਐΉͨΊPRͤͣ

    • ಈ͔ͳ͍෦෼Λ

    Hack࣮૷ͷϥΠϒϥϦͱೖΕସ͑Δ͜ͱʹ

    View full-size slide

  27. • PHPϥΠϒϥϦͷ࣮૷Λؾʹ͢Δͷ͸ɾɾɾ

    • PHPฒΈʹίʔυิ׬͕Ͱ͖ɺ

    HackͳΒͰ͸ͷ࣮૷Λ͢Δʹ͸HackͰ࡞Δ͔͠ͳ͍

    View full-size slide

  28. hhvm/hack-router

    View full-size slide

  29. • PSR-7ରԠͷHackઐ༻RouterϥΠϒϥϦ

    • Ҏલ͸fast-route֦ு͕ͩͬͨআ֎

    • GenericsΛ࢖͍ɺ

    Routerͷ࣮૷Λ஌Βͣʹར༻Ͱ͖ΔϥΠϒϥϦ

    • PHPαϙʔτΛ΍ΊΔ͜ͱʹ͋ͨΓɺ

    ͜ͷϥΠϒϥϦϕʔεʹҠߦ

    View full-size slide

  30. type TResponder = ImmVector>;
    final class Router extends BaseRouter {

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

    View full-size slide

  31. ytake/hh-container

    View full-size slide

  32. • PHPϥΠϒϥϦΛ࢖͍ଓ͚Δҙຯ΋͋·Γͳ͍ͨΊɺ

    Hackઐ༻ͷPimpleϥΠΫͳίϯςφΛ։ൃ

    • PSR-11 hhiΛ༻ҙ͠ɺHackͰ࣮૷

    • HackͳΒͰ͸ͷํ๏ͰSingleton͸ <<__Memoize>>

    • Πϯελϯεղܾํ๏ఆٛ͸ࣗ༝

    View full-size slide

  33. 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

    View full-size slide

  34. 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࣮૷

    View full-size slide

  35. 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෩
    Πϯελϯεੜ੒࣌ʹ࣮ߦ͢Δ

    View full-size slide

  36. • Zend ServiceManagerޓ׵Λ໨ࢦͨ͠ϥΠτ൛

    • Zend ExpressiveରԠͨ͠ͱ͜Ζɺ

    ಈతϝιουίʔϧ͸ਪ঑͞Ε͍ͯͳ͍ͨΊɺ

    Type Error
    • /* UNSAFE_EXPR */

    View full-size slide

  37. ໽հͳmixed
    PSR-11ͷྫ(strict)

    View full-size slide

  38. namespace Psr\Container;
    interface ContainerInterface
    {
    public function get(string $id): mixed;
    public function has(string $id): bool;
    }
    PSR-11 get (): mixed

    View full-size slide

  39. $container = require 'config/services.php';
    $app = $container->get('Zend\Expressive\Application');
    mixedͳͨΊɺԿ͕ฦ٫͞ΕΔ͔Θ͔Βͳ͍

    View full-size slide

  40. $config = $container->get('config_array');
    if (is_array($config)) {
    if (array_key_exists('config_array', $config)) {
    return $config['config_array'];
    }
    }
    arrayͰ͋Δ͜ͱΛࣔ͢

    View full-size slide

  41. $logger = $container->get(‘LoggerInterface');
    invariant(
    $logger instanceof LoggerInterface,
    "Interface '\Psr\Log\LoggerInterface' is not implemented by this class",
    );
    invariant͸HackͰ༻ҙ͞Ε͍ͯΔؔ਺
    ୈҰҾ਺ͷ৚͕݅falseͷ৔߹ʹType Error

    View full-size slide

  42. class TypeAssert {
    const type Tk = LoggerInterface;
    public static function assert(Tk $t): this::Tk {
    invariant(
    $logger instanceof LoggerInterface,
    "Interface '\Psr\Log\LoggerInterface' is not implemented by this class",
    );
    return $t;
    }
    }

    View full-size slide

  43. • ༷ʑͳ஋͕ฦ٫͞ΕΔͨΊɺmixed͸࢖͏΂͖Ͱ͸ͳ͍

    • PSRʹͩ͜ΘΔඞཁ΋ͳ͍

    (४ڌ͠ͳ͍͜ͱΛݕ౼த)

    • େن໛ͳ։ൃͳͲͰ͸ͳΔ΂͘Strict(ฐࣾஊ)


    View full-size slide

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

    View full-size slide

  45. hhvm/type-assert

    View full-size slide

  46. 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Λݕࠪ

    View full-size slide

  47. RouterͱContainerɺ
    ؆୯ͳValidation͕͋Ε͹
    ࠷௿ݶͷಈ࡞͕Մೳʹ

    View full-size slide

  48. Dependency / Container
    Build Application
    Routing/Dispatcher
    Middleware

    View full-size slide

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

    View full-size slide

  50. \Nazg\Http\HttpMethod::GET => ImmMap {
    '/' => ImmVector {App\Action\IndexAction::class},
    },
    GETʹରԠͤ͞ΔRouteΛهड़
    ‘/’ ʹΞΫηε࣌ʹىಈ͢Δϛυϧ΢ΣΞΛࢦఆ
    ImmVector>
    هड़ͨ͠௨Γʹ࣮ߦ͞ΕΔ

    ActionΫϥε΋ಉΠϯλʔϑΣʔε࣮૷ͷͨΊ۠ผͳ͠

    View full-size slide

  51. 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

    View full-size slide

  52. public function __construct(
    Traversable> $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> {
    return $this->queue->removeKey($index);
    }
    Collection (Vector)
    Vector ૢ࡞
    ҰͭͣͭऔΓग़࣮ͯ͠ߦ
    Vector ૢ࡞
    ࢦఆͨ͠ΠϯσοΫεͷΞΠςϜΛ࡟আ

    View full-size slide

  53. https://github.com/ytake/nazg-skeleton
    https://github.com/nazg-hack/framework

    View full-size slide

  54. • ࠷ۙͷPHPͱಉ༷ʹૄ݁߹ʹ
    • PSR-11, PSR-7, PSR-15Λར༻ͨͨ͠Ίɺ

    PHPͷϥΠϒϥϦ΋HackͰ࣮ߦՄೳͳ΋ͷ͸OK
    • router͸hack-routerҎ্ͷ΋ͷ͕ͳ͍ͨΊɺ

    hack-routerʹͷΈґଘ
    • PSR-7͸zend-diactorosΛσϑΥϧτʹ

    (Symfony Component 4Ҏ߱͸Hack ͸αϙʔτ͞Εͳ͍)

    View full-size slide

  55. • Request BodyͳͲͷValidationʹtype-assert

    • mixedΛέΞ͢Δ


    View full-size slide

  56. • Hack͔ͩΒԿ͔ಛผͳ΋ͷɺͱ͍͏ͷ͸ແ͍

    *ϥϯλΠϜӠʑ͸আ͘

    • όά͕গͳ͍ݎ࣮ͳΞϓϦέʔγϣϯ΁

    • ίʔυϨϏϡʔෛՙܰݮ
    • ΈΜͳ͕ࢥ͏΄Ͳ೉͘͠ͳ͍ʂ

    • PHPؾ෼Ͱ͔ΜͨΜͳϚΠΫϩϑϨʔϜϫʔΫ։ൃ

    View full-size slide

  57. • GoͰ΍Βͳ͔ͬͨͷ͔ʁ

    -> Go(Goa, Echo)͸ଟ͘ͷAPIͰಋೖࡁΈ

    େ͖͘ݴޠΛม͑ͣʹɺ

    PHPͷ։ൃऀ͕࢖͑Δ΋ͷΛ૿΍͔ͨͬͨ͠

    • segmentation faultͭΒ͘ͳ͍Ͱ͔͢ʁ

    hhvm-dbg + straceͰͳΜͱ͔ͳΓ·͢

    View full-size slide