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
Hackで作るマイクロフレームワーク
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
yuuki takezawa
March 06, 2018
Technology
7k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Hackで作るマイクロフレームワーク
For PHPerKaigi 2018
PHPからHackへ移行するときに気をつけるもの
PHPのライブラリを使ってフレームワークなどを作るにはどうしたらいいか、という内容です
yuuki takezawa
March 06, 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
330
PHPでアクターモデルを理解・体験しよう / Understand and experience the actor model in PHP
ytake
2
960
再考 アクターモデル/ 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
380
オブジェクトのおしゃべり大失敗 メッセージングアンチパターン集 / messaging anti-pattern collection
ytake
2
1.4k
DRE/SREのプラクティス融合によるクラウドネイティブなデータ基盤作り / dre_sre
ytake
0
1k
Other Decks in Technology
See All in Technology
凡エンジニアがこの先生きのこるためには。〜TypeScript完全に理解したい〜
alchemy1115
2
320
壊して学ぶAWS CDK: そのcdk deployで消えるもの、残るもの
k_adachi_01
1
430
AI Native なプロダクト組織の立ち上げ方 : 生産性 100 倍への挑戦
mikesorae
0
240
複数プロダクトで進めるAI機能実装 ── 実践から得たリアルな学びとロードマップ実現への挑戦 / AICon2026_yanari
rakus_dev
0
150
ソニー銀行におけるビジネスアジリティ向上のためのクラウドシフト戦略
srenext
0
870
「AIに依存している」と 「AIを使いこなしている」の違い
k8yasuma
0
120
副作用のある Lambda でも Lambda Power Tuning は使えるのか / lambda-power-tuning-side-effects
koukihosaka
1
120
「顧客の声を聞かなければ何も始まらない」 ── 顧客の声から生まれた『AI返信補助機能』の開発プロセス / AICon2026_shikata_imai
rakus_dev
0
150
AI時代の開発生産性を捉え直す — 経営と現場をつなぐ「開発組織のオブザーバビリティ」— / AI Dev Ex Conference 2026
tkyowa
0
200
ボーイスカウトルールでメモリやスキルを改善しよう
azukiazusa1
4
1.5k
企業でAWS Organizationsを動かすための組織設計の考え方
nrinetcom
PRO
1
110
SoccerMaster: A Vision Foundation Model for Soccer Understanding
kzykmyzw
0
150
Featured
See All Featured
How to Talk to Developers About Accessibility
jct
2
410
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
660
Building an army of robots
kneath
306
46k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.4k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
880
Skip the Path - Find Your Career Trail
mkilby
1
170
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
490
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
180
Transcript
HackͰ࡞Δ ϚΠΫϩϑϨʔϜϫʔΫ yuuki takezawa (ytake) PHPerKaigi 2018
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
author
None
None
ͳ͢͜ͱ • ։ൃ͢ΔͨΊͷࣝ .hhconfigɺdecl / strictɺhhi • ڧྗͳϥΠϒϥϦ hhvm/hhvm-autoloadɺhhvm/hack-router
• PHPϥΠϒϥϦͱͷޓΛอͭ։ൃ
<?hh ։ൃ͢ΔͨΊͷࣝ
<?hh Hackʁ • 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 ࢀর
<?hh Type checker • Ϟʔυ3ͭ Partial / Strict / Decl
• σϑΥϧτͰPartial
<?hh Type Check: Partial • PHPͷܕએݴ strictͱಉఔ • ඞཁҎ্ʹܕνΣοΫ͠ͳ͍ •
ओʹPHPͷίʔυΛ Hackͱ࣮ͯ͠ߦ͢Δ ίʔυҠ২தͷϑΝΠϧͰར༻ • ࢀর͠ ར༻Մೳ
<?hh Type Check: Decl • <?hh // decl • ܕνΣοΫ͠ͳ͍
• ଞͷίʔυνΣοΫ࣌ʹࢀর͞ΕΔ • New Hack code should never be written in decl mode
<?hh Type Check: Strict • <?hh // strict • PHPґଘ͕ͳ͘ɺ100%
HackͰ࣮͢Δ߹ʹબ • ݫ֨ͳܕνΣοΫΛߦ͏ϞʔυͷͨΊɺ ίʔυϨϏϡʔ࣌ͷܕએݴʹ͍ͭͯͷٞͳ͠ • ఆ֎ͷܕมͳͲߦΘΕͳ͍ͨΊɺ ϨϏϡʔΫϥεઃܭɾΞʔΩςΫνϟͳͲʹ ϑΥʔΧεͰ͖Δ • Type Checkerʹڭ͑ΔͨΊͷίʔυʹ
<?hh Type Check: Strict • ࠷ݫ֨ͳϞʔυͷͨΊɺ PHPϥΠϒϥϦΛ͏ίʔυͰܕએݴΤϥʔൃੜ => hhiϑΝΠϧΛ࡞(ޙड़) •
__invokeͷѻ͍͕PHPͱҟͳΔͨΊܕΤϥʔൃੜ => strictͰ__invokeͰॲཧ͢ΔϛυϧΣΞ(ඇPSR-15) ΛऔΓೖΕΔ߹ҙ • HackͰ࣮͢ΔͷͰ͋ΕStrictΛج४ʹɺ Ұ෦͚ͩPartial͕ϕετ
<?hh callableʹؾΛ͚ͭΔ • PHPͰ callableͰܕએݴ͞Ε͍ͯΔͷClosureɺ ·ͨ__invokeϝιουΛ࣋ͭΫϥεͰ͋Εྑ͍ • HackͰcallableͰͳ͘ɺແ໊ؔͷҾܕએݴ __invokecallableѻ͍Ͱͳ͍ ->
PHPͷ __invoke Λ࣮͢ΔϛυϧΣΞܥΛ ͦͷ··strictͰҠ২Ͱ͖ͳ͍ • inst_meth ͱ͍͏બࢶ
class Example { public function foo<T>(T $t): T { return
$t; } } inst_meth(new Example(), ‘foo’); Must be a constant string.
<?hh hhi
<?hh // strict • TypeScript, flowͱಉ༷ͳܕఆٛϑΝΠϧ • ୯ମͰ࣮ߦͰ͖ͳ͍ • ܕఆٛϑΝΠϧΛ࡞Δ͜ͱͰɺܕએݴΛߦ͍ɺ
ݫ֨ϞʔυͰ࣮ߦ͢Δ͜ͱ͕Մೳ • ͍͍ͨPHPͷίʔυิͯ͠΄͍͠ʂʂ ͱ͍͏ํ࡞ͯ͠packagistͰެ։͍ͯͩ͘͠͞ʂ
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 ʹؚ·Ε͍ͯ·͢
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.
<?hh hhi PSR-11 Container Interface : Example
<?hh // strict namespace Psr\Container; interface ContainerInterface { public function
get(string $id): mixed; public function has(string $id): bool; } ΓͷܕએݴΛՃ͚ͨͩ͠
<?hh .hhconfig
<?hh .hhconfig • HackͰ࣮ߦڥʹઃஔ͢ΔϑΝΠϧ • ࣮༷ʑͳઃఆΛهड़Ͱ͖Δ • PHPར༻Λఆ͠ͳ͍(PHPࠞࡏෆՄ) assume_php
= false(default: true) • Type Checker Ұ෦ແࢹ ignored_paths = [ "vendor/hhvm/hhast/.+" ]
<?hh ڧྗͳϥΠϒϥϦ
<?hh hhvm/hhvm-autoload
<?hh HHVM-Autoload • 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Λઃஔ
<?hh HHVM-Autoload • લड़ͨ͠hhiϑΝΠϧͳͲΛvendorσΟϨΫτϦ͔Β ݟ͚ͭग़͠ɺTypeCheckerରԠ͚ͩͰͳ͘ɺ Nuclide, VSCͷิͱͯ͠࡞༻ • HackͰ࣮͢Δ߹ඞͣೖΕ͓͖ͯ·͠ΐ͏
<?hh Composer install • 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
Zend Expressive with HHVM/Hack
<?hh Zend Expressive 1.0 • HHVM/HackͰɺԿؾʹͤͣར༻Մೳ • Zend ServiceManager +
HHVM/HackͰ࣮ɾՔಇ • PHPײ֮ͰखܰʹHHVM/HackΞϓϦέʔγϣϯΛ։ൃ ͢Δ߹ʹΦεεϝ
<?hh Zend Expressive >= 2.0 • zend-config-aggregator ͷҰ෦ͷίʔυ͕ಈ࡞ͤͣ __invoke, yield͕ಈ͔ͣ
(Generator<Tk, +Tv, -Ts>) • fast-routeͷhhi͕ϝϯς͞Ε͍ͯͳ͍ͨΊType Error PHPͱͷ͕ਐΉͨΊPRͤͣ • ಈ͔ͳ͍෦Λ Hack࣮ͷϥΠϒϥϦͱೖΕସ͑Δ͜ͱʹ
<?hh • PHPϥΠϒϥϦͷ࣮Λؾʹ͢Δͷɾɾɾ • PHPฒΈʹίʔυิ͕Ͱ͖ɺ HackͳΒͰͷ࣮Λ͢ΔʹHackͰ࡞Δ͔͠ͳ͍
<?hh hhvm/hack-router
<?hh hack-router • PSR-7ରԠͷHackઐ༻RouterϥΠϒϥϦ • Ҏલfast-route֦ு͕ͩͬͨআ֎ • GenericsΛ͍ɺ Routerͷ࣮ΛΒͣʹར༻Ͱ͖ΔϥΠϒϥϦ •
PHPαϙʔτΛΊΔ͜ͱʹ͋ͨΓɺ ͜ͷϥΠϒϥϦϕʔεʹҠߦ
type TResponder = ImmVector<classname<MiddlewareInterface>>; final class Router extends BaseRouter<TResponder> {
<<__Override>> protected function getRoutes( ): ImmMap<HackRouterHttpMethod, ImmMap<string, TResponder>> { $i = $this->routeMap->getIterator(); $map = []; while ($i->valid()) { $map[$this->convertHttpMethod($i->key())] = $i->current(); $i->next(); } return new ImmMap($map); } ࢦఆͨ͠ΫϥεɺIFΛܧঝɺ࣮ͨ͠Ϋϥε໊એݴ Routerʹ֨ೲ͠ɺϚον࣌ʹTResponderΛฦ٫
<?hh ytake/hh-container
<?hh PSR-11 • PHPϥΠϒϥϦΛ͍ଓ͚Δҙຯ͋·Γͳ͍ͨΊɺ Hackઐ༻ͷPimpleϥΠΫͳίϯςφΛ։ൃ • PSR-11 hhiΛ༻ҙ͠ɺHackͰ࣮ • HackͳΒͰͷํ๏ͰSingleton
<<__Memoize>> • Πϯελϯεղܾํ๏ఆٛࣗ༝
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
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࣮
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෩ Πϯελϯεੜ࣌ʹ࣮ߦ͢Δ
<?hh PSR-11 • Zend ServiceManagerޓΛࢦͨ͠ϥΠτ൛ • Zend ExpressiveରԠͨ͠ͱ͜Ζɺ ಈతϝιουίʔϧਪ͞Ε͍ͯͳ͍ͨΊɺ Type
Error • /* UNSAFE_EXPR */
հͳmixed PSR-11ͷྫ(strict)
<?hh // strict namespace Psr\Container; interface ContainerInterface { public function
get(string $id): mixed; public function has(string $id): bool; } PSR-11 get (): mixed
$container = require 'config/services.php'; $app = $container->get('Zend\Expressive\Application'); mixedͳͨΊɺԿ͕ฦ٫͞ΕΔ͔Θ͔Βͳ͍
$config = $container->get('config_array'); if (is_array($config)) { if (array_key_exists('config_array', $config)) {
return $config['config_array']; } } arrayͰ͋Δ͜ͱΛࣔ͢
$logger = $container->get(‘LoggerInterface'); invariant( $logger instanceof LoggerInterface, "Interface '\Psr\Log\LoggerInterface' is
not implemented by this class", ); invariantHackͰ༻ҙ͞Ε͍ͯΔؔ ୈҰҾͷ͕݅falseͷ߹ʹType Error
class TypeAssert { const type Tk = LoggerInterface; public static
function assert<Tk>(Tk $t): this::Tk { invariant( $logger instanceof LoggerInterface, "Interface '\Psr\Log\LoggerInterface' is not implemented by this class", ); return $t; } }
<?hh mixed • ༷ʑͳ͕ฦ٫͞ΕΔͨΊɺmixed͏͖Ͱͳ͍ • PSRʹͩ͜ΘΔඞཁͳ͍ (४ڌ͠ͳ͍͜ͱΛݕ౼த) • େنͳ։ൃͳͲͰͳΔ͘Strict(ฐࣾஊ)
Forget PHP! Facebook’s HHVM engine switches to Hack instead
<?hh hhvm/type-assert
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Λݕࠪ
RouterͱContainerɺ ؆୯ͳValidation͕͋Ε ࠷ݶͷಈ࡞͕Մೳʹ
Dependency / Container Build Application Routing/Dispatcher Middleware
type TResponder = ImmVector<classname<\Psr\Http\Server\MiddlewareInterface>>; hack routerͷTResponder ϦΫΤετʹରԠ͢Δͷ͕ొ͞ΕͯೖΕɺ TResponderͰࢦఆͨ͠ܕΛฦ٫͢Δ
\Nazg\Http\HttpMethod::GET => ImmMap { '/' => ImmVector {App\Action\IndexAction::class}, }, GETʹରԠͤ͞ΔRouteΛهड़
‘/’ ʹΞΫηε࣌ʹىಈ͢ΔϛυϧΣΞΛࢦఆ ImmVector<classname<\Psr\Http\Server\MiddlewareInterface>> هड़ͨ͠௨Γʹ࣮ߦ͞ΕΔ ActionΫϥεಉΠϯλʔϑΣʔε࣮ͷͨΊ۠ผͳ͠
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
public function __construct( Traversable<classname<MiddlewareInterface>> $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<classname<MiddlewareInterface>> { return $this->queue->removeKey($index); } Collection (Vector) Vector ૢ࡞ ҰͭͣͭऔΓग़࣮ͯ͠ߦ Vector ૢ࡞ ࢦఆͨ͠ΠϯσοΫεͷΞΠςϜΛআ
https://github.com/ytake/nazg-skeleton https://github.com/nazg-hack/framework
<?hh খ͞ͳϥΠϒϥϦͷू߹ମ • ࠷ۙͷPHPͱಉ༷ʹૄ݁߹ʹ • PSR-11, PSR-7, PSR-15Λར༻ͨͨ͠Ίɺ PHPͷϥΠϒϥϦHackͰ࣮ߦՄೳͳͷOK •
routerhack-routerҎ্ͷͷ͕ͳ͍ͨΊɺ hack-routerʹͷΈґଘ • PSR-7zend-diactorosΛσϑΥϧτʹ (Symfony Component 4Ҏ߱Hack αϙʔτ͞Εͳ͍)
<?hh খ͞ͳϥΠϒϥϦͷू߹ମ • Request BodyͳͲͷValidationʹtype-assert • mixedΛέΞ͢Δ
<?hh • Hack͔ͩΒԿ͔ಛผͳͷɺͱ͍͏ͷແ͍ *ϥϯλΠϜӠʑআ͘ • όά͕গͳ͍ݎ࣮ͳΞϓϦέʔγϣϯ • ίʔυϨϏϡʔෛՙܰݮ • ΈΜͳ͕ࢥ͏΄Ͳ͘͠ͳ͍ʂ
• PHPؾͰ͔ΜͨΜͳϚΠΫϩϑϨʔϜϫʔΫ։ൃ
<?hh ͓·͚ • GoͰΒͳ͔ͬͨͷ͔ʁ -> Go(Goa, Echo)ଟ͘ͷAPIͰಋೖࡁΈ େ͖͘ݴޠΛม͑ͣʹɺ PHPͷ։ൃऀ͕͑ΔͷΛ૿͔ͨͬͨ͠ •
segmentation faultͭΒ͘ͳ͍Ͱ͔͢ʁ hhvm-dbg + straceͰͳΜͱ͔ͳΓ·͢