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

HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション

HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション

HHVM/Hack勉強会Vol.1 slide
https://istyle.connpass.com/event/80398/

yuuki takezawa

April 23, 2018
Tweet

More Decks by yuuki takezawa

Other Decks in Programming

Transcript

  1. <?hh // strict class Serialize { public function execute(shape( 'message'

    => string, 'date' => string, ) $shape): string { return serialize($shape); } }
  2. public function executeCollection(shape( 'message' => string, 'date' => string, 'tables'

    => Vector<CollectableShape>, ) $shape): string { $v = Shapes::idx($shape, 'tables', Vector{ }); return serialize( array_merge( Shapes::toArray($shape), ['tables' => $v->toArray()] ) ); }
  3. function nullthrows<T>( ?T $data, string $message = 'unexpected null’ ):

    T { invariant( $data !== null, $message, ); return $data; }
  4. <?hh // strict 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, /* associative = */ true); return TypeAssert\matches_type_structure( type_structure(self::class, 'TAPIResponse'), $array, ); } }
  5. json decodeʹؔͯ͠ • ίʔϧͨ͠APIͷ໭Γ஋ͳͲʹ΋࢖͏͜ͱ͕Ͱ͖Δ
 • JSONʹؔͯ͠͸json_decodeͰ
 ͍͔ͭ͘Hack޲͚Φϓγϣϯ͕͋Γ
 • JSON_FB_COLLECTIONS =>

    Map
 JSON_FB_HACK_ARRAYS => dict
 https://github.com/facebook/hhvm/blob/master/hphp/ hack/hhi/stdlib/builtins_json.hhi#L41
  6. ࣮ફ • ΞΠελΠϧͰ͸HAL - Hypertext Application Language Λ࠾༻͍ͯ͠Δ
 • ߏ଄ΛshapeͰදݱ͢Δ

    • https://techblog.istyle.co.jp/2018/04/18/ hack%E3%81%A7%E4%BD%9C%E3%82%8B%E5%A0 %85%E5%AE%9F%E3%81%AAapi/

  7. // goal { "id":1234, "name":"ytake", "title":"type-assert for api response", "_links":{

    "self":{ “href":"/", "type":"application/json" } }, "_embedded":{ "enviroments":[ { "name":"HHVM/Hack", "_links":{ "self":{ "href":"https://docs.hhvm.com/" } } } ] } }
  8. // shapeͰhal+jsonΛදݱ͢Δ const type embeddedLinks = shape( 'name' => string,

    '_links' => shape( 'self' => shape( 'href' => string ) ) ); const type hateoasStructure = shape( 'id' => int, 'name' => string, 'title' => string, '_links' => shape( 'self' => shape( 'href' => string, 'type' => string ) ), '_embedded' => shape( 'enviroments' => array<self::embeddedLinks> ) );
  9. // ར༻ྫɹ͜͜Ͱ͸ImmMap new ImmMap([ 'id' => 1234, 'name' => 'ytake',

    'title' => 'type-assert for api response', 'embedded' => [ [ 'name' => 'HHVM/Hack', 'url' => 'https://docs.hhvm.com/' ], ] ]);
  10. $map = $this->resource ->filterWithKey(($k, $v) ==> $k != 'embedded') ->toMap();

    $hal = new HalResource($map); $hal->withLink(new Link( 'self', new Vector([ new LinkResource( '/', shape('type' => 'application/json')) ]), )); // hal+jsonରԠϥΠϒϥϦ࣮૷ྫ (Hack)
  11. $embedded = $this->resource->get('embedded'); if(is_array($embedded)) { foreach($embedded as $row) { $embeddedResource

    = new HalResource(); foreach($row as $key => $value) { if($key === 'url') { $embeddedResource->withLink( new Link('self', new Vector([new LinkResource($value)])) ); continue; } $embeddedResource->addResource(strval($key), $value); } $this->vec->add($embeddedResource); } }
  12. public function process( ServerRequestInterface $request, RequestHandlerInterface $handler, ): ResponseInterface {

    $response = $handler->handle($request); // ϛυϧ΢ΣΞͷޙ൒Ͱಈ͘ $decode = json_decode($response->getBody()->getContents(), true); TypeAssert\matches_type_structure( type_structure(self::class, 'hateoasStructure'), $decode, ); return $response; }
  13. <?hh // stirct use Psr\Log\LoggerInterface; use Ytake\HHContainer\Scope; use Ytake\HHContainer\ServiceModule; use

    Ytake\HHContainer\FactoryContainer; use App\Middleware\ResponseAssertMiddleware; final class MiddlewareServiceModule extends ServiceModule { public function provide(FactoryContainer $container): void { $container->set( ResponseAssertMiddleware::class, $container ==> new ResponseAssertMiddleware() ); // container΁ͷόΠϯυྫ } }
  14. // route΋shapeͰෆ࣮֬͞Λͳ͘͢ʂ \Nazg\Http\HttpMethod::GET => ImmMap { '/' => shape( 'middleware'

    => ImmVector { App\Middleware\ResponseAssertMiddleware::class, App\Action\IndexAction::class, }, ) },