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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
370
PHPでアクターモデルを理解・体験しよう / Understand and experience the actor model in PHP
ytake
2
990
再考 アクターモデル/ reconsider actor model
ytake
0
1.7k
GoとアクターモデルでES+CQRSを実践! / proto_actor_es_cqrs
ytake
1
680
Phluxorでアクターモデルを 理解・体験しよう / toolkit-for-flexible-actor-models-in-php-phluxor
ytake
1
400
オブジェクトのおしゃべり大失敗 メッセージングアンチパターン集 / messaging anti-pattern collection
ytake
2
1.4k
DRE/SREのプラクティス融合によるクラウドネイティブなデータ基盤作り / dre_sre
ytake
0
1.1k
Other Decks in Programming
See All in Programming
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
150
Omarchy Tokyo やると聞いて UMPC 買ってセットアップしてきた
mtsmfm
0
140
世界の中心で、AI(App Intents)をさけぶ ー App Intents中心設計の実践ガイド
touyou
0
450
AIとGame Jamで、ゲームを完成させた話
takahirosaeki
0
120
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
2.8k
Foundry Localでエージェント開発
seosoft
0
190
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
4.1k
[DroidKaigi 2026] Bring your own phones to Gradle Managed Devices
f2lk
0
120
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
370
Swift愛好会と私(ウホーイ) / Swift Fan Club and Uhooi
uhooi
0
150
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
150
GKE で Pod の見方を変えたら、スケールアウト時の挙動を真に捉えられた話
stkk
0
110
Featured
See All Featured
The Curious Case for Waylosing
cassininazir
1
500
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
12k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
230
BBQ
matthewcrist
89
10k
The Limits of Empathy - UXLibs8
cassininazir
1
660
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
410
Accessibility Awareness
sabderemane
1
210
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
540
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
A Soul's Torment
seathinner
7
3.6k
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Ͱఏڙͯ͠Έ·͠ΐ͏