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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
2.5k
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
320
PHPでアクターモデルを理解・体験しよう / Understand and experience the actor model in PHP
ytake
2
930
再考 アクターモデル/ reconsider actor model
ytake
0
1.6k
GoとアクターモデルでES+CQRSを実践! / proto_actor_es_cqrs
ytake
1
660
Phluxorでアクターモデルを 理解・体験しよう / toolkit-for-flexible-actor-models-in-php-phluxor
ytake
1
380
オブジェクトのおしゃべり大失敗 メッセージングアンチパターン集 / messaging anti-pattern collection
ytake
2
1.3k
DRE/SREのプラクティス融合によるクラウドネイティブなデータ基盤作り / dre_sre
ytake
0
1k
Other Decks in Technology
See All in Technology
AAIFに入ってみた ~内から見えるコミュニティ動向~
sato4
0
130
個人最適 から 全体最適 へ AI情報共有会・AIギルド・AI-DLC で進める カンリーの組織展開
rfdnxbro
0
2.1k
「気づいたら仕事が終わっている」バクラクAIエージェント本番運用の裏側 / layerx-bakuraku-aie2026
yuya4
19
12k
スキルと MCP ツール、責務をどう分けるか? AI が迷わないインターフェース設計の戦略
cdataj
1
890
やさしいA2A入門
minorun365
PRO
10
1.6k
OCI Oracle AI Database Services新機能アップデート(2026/03-2026/05)
oracle4engineer
PRO
0
350
Building applications in the Gemini API family.
line_developers_tw
PRO
0
2.8k
データ基盤をDataformで整えた話 〜 開発環境を添えて 〜
takapy
0
140
中期計画、2回作ってみた ~業務委託と正社員、両方の視点から~
demaecan
1
620
"何を作るか"を任される エンジニアは、どう育つのか
yutaokafuji
1
540
ルールやカスタム機能、どう活かす?ハンズオンで体感するIBM Bobの出力コントロール
muehara
1
120
非エンジニアがClaudeと挑んだ「1ヶ月間プロダクト30本ノック」
askokc
0
220
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
170
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
550
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Code Review Best Practice
trishagee
74
20k
Abbi's Birthday
coloredviolet
2
8k
Claude Code のすすめ
schroneko
67
230k
Are puppies a ranking factor?
jonoalderson
1
3.5k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
340
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ͰͳΜͱ͔ͳΓ·͢