Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
yuuki takezawa
April 23, 2018
Programming
720
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション
HHVM/Hack勉強会Vol.1 slide
https://istyle.connpass.com/event/80398/
yuuki takezawa
April 23, 2018
More Decks by yuuki takezawa
See All by yuuki takezawa
なぜAI時代に 「イベント」を中心に考えるのか? / Why focus on "events" in the age of AI?
ytake
4
2.2k
PHPでアクターモデルを活用したSagaパターンの実践法 / php-saga-pattern-with-actor-model
ytake
0
3.1k
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
390
PHPでアクターモデルを理解・体験しよう / Understand and experience the actor model in PHP
ytake
2
1k
再考 アクターモデル/ reconsider actor model
ytake
0
1.7k
GoとアクターモデルでES+CQRSを実践! / proto_actor_es_cqrs
ytake
1
740
Phluxorでアクターモデルを 理解・体験しよう / toolkit-for-flexible-actor-models-in-php-phluxor
ytake
1
410
オブジェクトのおしゃべり大失敗 メッセージングアンチパターン集 / messaging anti-pattern collection
ytake
2
1.4k
DRE/SREのプラクティス融合によるクラウドネイティブなデータ基盤作り / dre_sre
ytake
0
1.1k
Other Decks in Programming
See All in Programming
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
0
450
R8 の設定見直しでアプリサイズ削減
andpad
0
220
sbt 2.0: takeoff
eed3si9n
0
160
GemmaをJevのように使ってみる / Use Gemma like Jev
kishida
5
790
コードで差別化できない時代の ソフトウェアエンジニアリング / Software Engineering in an Era Where Code Doesn’t Differentiate
kishida
2
180
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
1.1k
AIに “答え“を書かせるのを やめてみた
___akari11
0
120
AIで開発が速くなった先で、開発チームは何を目指すか
shihochan
0
880
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
2k
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
140
golang-bob.pdf
iwatsukayura
0
130
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
270
Featured
See All Featured
Abbi's Birthday
coloredviolet
4
10k
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
Building AI with AI
inesmontani
PRO
1
1.3k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3.1k
BBQ
matthewcrist
90
10k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
410
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
Typedesign – Prime Four
hannesfritz
42
3.2k
Facilitating Awesome Meetings
lara
57
7.2k
Darren the Foodie - Storyboard
khoart
PRO
4
4k
The Invisible Side of Design
smashingmag
301
52k
Making Projects Easy
brettharned
120
6.8k
Transcript
shapeͱTypeAssertͰ ݎ࿚ͳΞϓϦέʔγϣϯ yuuki takezawa (ytake)
εϥΠυͰར༻͍ͯ͠Δαϯϓϧίʔυ https://github.com/istyle-inc/example-hack- strict-api
<?hh ෆ࣮֬ͳAPI
Ͳ͏ͬͯܕΛࢦఆ͢Δ͔ • Collection (Map, Vector etc) • Generics • shape
• tuple
[ ‘message’ => ‘hackstudy’, ‘date’ => ‘2018/04/20’ ];
ྻอূ͞ΕΔ͔ • ͍ΘΏΔPHPͷྻɺܕͷ੍͕ͳ͍ ࣗ༝ͳϋογϡςʔϒϧ • ྻͰ͋Εserialize -> json response •
messageʹint͕ೖͬͨΒʁ • date͕intͩͬͨΒʁ • Ωʔ͕૿͑ͨΓݮͬͨΓ • typo?
ෆ࣮֬Ͱ͔͠ͳ͍ʂ
<?hh shape
shape( 'message' => string, 'date' => string, );
<?hh // strict class Serialize { public function execute(shape( 'message'
=> string, 'date' => string, ) $shape): string { return serialize($shape); } }
$serialize = new \Serialize(); $serialize->execute(shape( 'message' => 'hackstudy', 'date' =>
'2018/04/20', ));
shapeΛͯ͠େৎ • ྻͱಉʹར༻͢Δ͜ͱ͕Ͱ͖ΔͷͰɺ ઃఆͳͲΛ͢ɺ֎෦Ϧιʔεʹର੍ͯ͠Λ͔͚Δ ͳͲʹ༻్ʹόονϦ • foreachshapeʹର࣮ͯ͠ߦͰ͖ͳ͍ • it doesn't
implement Traversable or Container. • []͑·ͤΜ • toArrayͰมͯ͠ྑ͍
foreach͍ͨ͠ΜͰ͚͢Ͳ
<?hh shape + Collection
type CollectableShape = shape( 'id' => int, 'name' => string,
);
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()] ) ); }
CollectionΛΈ߹Θͤͯଟछଟ༷ʹ • ྻʹมͨ͠Γɺ͘͠ྻΛදݱͨ͠Γ • ৭ʑͳ༻్Ͱ৭ʑ੍Λ͔͚Δ͜ͱ͕Ͱ͖Δ • Ͱshape
֎෦ϦιʔεshapeͰͳ͍
function nullthrows<T>( ?T $data, string $message = 'unexpected null’ ):
T { invariant( $data !== null, $message, ); return $data; }
APIͷΓͷνΣοΫʁ • ྻʹมͨ͋͠ͱʹgenerics+invariant͢Δʁ • ҰͭҰͭεΩϟϯʁ
hhvm/type-assert
ෆ࣮֬ͳΛΞαʔτʂ • primitiveܕͪΖΜɺmapͳͲʹ • is_aͳͲநԽ • Ұ൪ͷۄ matches_type_structure
<?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, ); } }
matches_type_structure • ReflectionTypeAlias ΫϥεͷgetTypeStructureϝιουྻΛ ฦͨ͢Ίਏ͍(HackͳΒجຊstirctͰॻ͘͜ͱ͕ଟ͍) https://github.com/facebook/hhvm/blob/master/hphp/hack/hhi/ reflection.hhi#L308 • ܕ௨ΓͩͬͨΒͦͷ··௨աͯ͠ཉ͍͠ͷͰɺ Genericsʹͯ͠΄͍͠ʂ
type_structure ؔར༻ͷΈରԠ https://github.com/facebook/hhvm/blob/master/hphp/hack/hhi/ typestructure.hhi#L94
matches_type_structure • ݕࠪ͢Δܕconst stringͰ͋Δඞཁ͕͋ΔͷͰɺ ಈతʹੜͰ͖ͳ͍ • shapeͰͻͨ͢Βॻ͔͘͠ͳ͍
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
ΞϓϦέʔγϣϯ͕ฦ٫͢Δͷʹ ద༻ ݎ࿚ͳϨεϙϯεΛఏڙ͢Δ
ΞϓϦέʔγϣϯͰ࣮༻͢Δʹ • HackͳΒϚΠΫϩαʔϏεʹ͓͚ΔAPI Gatewayͱ͔ • ฦ٫͢ΔΛ୲อͯ͋͛͠ΔΑ͏ʹͨ͠΄͏͕ྑ͍ • ϞμϯͳͷͰ͋ΕϛυϧΣΞΛ༻ҙͯ͠ɺ ࣗΒͷϨεϙϯεΛௐΔ
ΦϨΦϨHackϑϨʔϜϫʔΫͰ ΈࠐΜͰΈͨ https://github.com/ytake/nazg-skeleton
࣮ફ • ΞΠελΠϧͰ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/
// 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/" } } } ] } }
// 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> ) );
// ར༻ྫɹ͜͜ͰImmMap new ImmMap([ 'id' => 1234, 'name' => 'ytake',
'title' => 'type-assert for api response', 'embedded' => [ [ 'name' => 'HHVM/Hack', 'url' => 'https://docs.hhvm.com/' ], ] ]);
࣮ફ • ImmMapΛड͚औΓhal+jsonʹม • HalରԠϥΠϒϥϦPHPͰ͍͔ͭ͋͘Δ HackPHP͔ΒΕ͍ͯͬͯΔͨΊɺ PHPϥΠϒϥϦ͏ཧ༝ͳ͍ͷͰࣗͰ࡞Δ(ͨ) • https://github.com/ytake/hhhal
protected Vector<HalResource> $vec = Vector{}; public function __construct( protected ImmMap<mixed,
mixed> $resource ) {}
$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)
$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); } }
// γϦΞϥΠζ͢Δͱhal+jsonରԠͷjson͕ग़ྗ͞Ε·͢(Hack) $hal = $hal->withEmbedded('enviroments', $this->vec); $serialize = new Serializer(new
JsonSerializer(), $hal); return $serialize->toArray();
࣮ફ • ϛυϧΣΞͷ࠷ޙʹ࣮ߦ͢ΔΑ͏ʹ͢Δ • PSR-15४ڌͷϛυϧΣΞͰ͋ΕͳΜͰ
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; }
<?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ͷόΠϯυྫ } }
// routeshapeͰෆ࣮֬͞Λͳ͘͢ʂ \Nazg\Http\HttpMethod::GET => ImmMap { '/' => shape( 'middleware'
=> ImmVector { App\Middleware\ResponseAssertMiddleware::class, App\Action\IndexAction::class, }, ) },
·ͱΊ • strictҎ֎ͭΒ͍ • mixedͭΒ͍ • ϚΠΫϩαʔϏεͰೖ͢ΔͳΒtype-assertΛ͓קΊ • inputͱoutput ৴༻͍͚͗ͯ͢͠ͳ͍
• null͕ʂܕ͕ʂͱݴΘΕͨΒHackͰఏڙͯ͠Έ·͠ΐ͏