Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション
Search
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.1k
PHPでアクターモデルを活用したSagaパターンの実践法 / php-saga-pattern-with-actor-model
ytake
0
3k
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
340
PHPでアクターモデルを理解・体験しよう / Understand and experience the actor model in PHP
ytake
2
970
再考 アクターモデル/ reconsider actor model
ytake
0
1.6k
GoとアクターモデルでES+CQRSを実践! / proto_actor_es_cqrs
ytake
1
670
Phluxorでアクターモデルを 理解・体験しよう / toolkit-for-flexible-actor-models-in-php-phluxor
ytake
1
390
オブジェクトのおしゃべり大失敗 メッセージングアンチパターン集 / messaging anti-pattern collection
ytake
2
1.4k
DRE/SREのプラクティス融合によるクラウドネイティブなデータ基盤作り / dre_sre
ytake
0
1.1k
Other Decks in Programming
See All in Programming
運用ダッシュボードの設計を誰も教えてくれないのだけどみなさんどうしてるんですか? - チームに監視するという文化を根付かせるための第一歩を踏みたい -
satoshi256kbyte
1
110
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
630
プロポーザルを書いてもらう
pvcresin
0
430
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.7k
夏だ!祭りだ!祭りとはドメインモデリングでは?
ryugen04
0
230
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
140
生成AI導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
4.3k
Detecting Compromised CI with eBPF and Cilium Tetragon
lizrice
0
170
freeeにおけるEvalsの実践例の紹介
freee
PRO
0
110
TSX の <Hoge<Fuga>> という構文に驚いた話 / tsx-type-argument-syntax
kanaru0928
0
220
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
400
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
140
Featured
See All Featured
So, you think you're a good person
axbom
PRO
2
2.1k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
470
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
640
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Paper Plane (Part 1)
katiecoart
PRO
1
10k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
460
Navigating Weather and Climate Data
rabernat
0
470
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
260
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
240
First, design no harm
axbom
PRO
2
1.2k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
460
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Ͱఏڙͯ͠Έ·͠ΐ͏