Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Hackで作るマイクロフレームワーク
yuuki takezawa
March 06, 2018
Technology
4
5.8k
Hackで作るマイクロフレームワーク
For PHPerKaigi 2018
PHPからHackへ移行するときに気をつけるもの
PHPのライブラリを使ってフレームワークなどを作るにはどうしたらいいか、という内容です
yuuki takezawa
March 06, 2018
Tweet
Share
More Decks by yuuki takezawa
See All by yuuki takezawa
入門 境界づけられたコンテキスト
ytake
6
2.8k
時間軸とドメインイベントとデータ処理
ytake
1
1.2k
事業のスケールアウトを支える PHPで作る分散アーキテクチャ
ytake
4
3.7k
Hack HTTP Request and Response Interfaces
ytake
0
6.1k
Hackで作る堅実な アプリケーションアーキテクチャ / Hack-application-architecture
ytake
3
7.3k
PHPとApache Sparkで始めるデータ解析処理 / php-with-apache-spark
ytake
2
3.2k
Event Sourcing+CQRS を活用するスケーラブルアプリケーション開発 / event-sourcing-cqrs-v2
ytake
7
11k
大きなデータと戦う技術 / fighting-large-data
ytake
3
410
Laravel JP Conference2019
ytake
1
1.7k
Other Decks in Technology
See All in Technology
ラズパイとGASで加湿器の消し忘れをLINEでリマインド&操作
minako__ph
0
150
Oracle Cloud Infrastructure:2023年1月度サービス・アップデート
oracle4engineer
PRO
0
160
金属加工屋の営業マンがSTマイクロで・・・
usashirou
0
160
Logbii(ログビー) 会社紹介
logbii
0
140
Exploring MapStore Release 2022.02: improved 3DTiles support and more
simboss
PRO
0
370
組織に対してSREを適用するとどうなるか
kuniim
7
2.8k
DNS権威サーバのクラウドサービス向けに行われた攻撃および対策 / DNS Pseudo-Random Subdomain Attack and mitigations
kazeburo
5
1.3k
エアドロップ for オープンソースプロジェクト
epicsdao
0
390
あつめたデータをどう扱うか
skrb
2
160
OpenShiftのリリースノートを整理してみた
loftkun
2
390
OVN-Kubernetes-Introduction-ja-2023-01-27.pdf
orimanabu
1
390
KyvernoとRed Hat ACMを用いたマルチクラスターの一元的なポリシー制御
ry
0
180
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
261
26k
We Have a Design System, Now What?
morganepeng
37
5.9k
Building an army of robots
kneath
301
40k
Become a Pro
speakerdeck
PRO
6
3.2k
Gamification - CAS2011
davidbonilla
75
4.1k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
22
1.7k
Statistics for Hackers
jakevdp
785
210k
Streamline your AJAX requests with AmplifyJS and jQuery
dougneiner
128
8.8k
Designing for Performance
lara
600
65k
The Illustrated Children's Guide to Kubernetes
chrisshort
22
43k
KATA
mclloyd
12
9.7k
Build The Right Thing And Hit Your Dates
maggiecrowley
22
1.4k
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ͰͳΜͱ͔ͳΓ·͢