ytake blog

Web Application Developer

2018年振り返り

総括

今年は仕事の面でも、開発者としての面でも
いろんな出来事があって全体的にものすごく忙しい一年だった(毎年なんですけど)

blog.ytake.jp.net

登壇というアウトプットはしていた一年だったが、
ブログを書く時間を取れなかった。
が、開発に使う言語の比率がいろんな面で大きく割合が変わった。
2017年まで PHP(Hack含む 50%)、Go(30%)、他という感じだったが、 2018年は PHP(ほとんどHack 40%)、Scala(30%)、Go(20%)他と、
やりたかったScalaを選択する時間が増えてきた。
2018年はRDBMSとか所謂LAMP環境の開発がほとんどなくなり、
Apache Hadoop、Apache Spark、Apache Kafka、Apache Solr、Apache Cassandra、
AWSと機械学習、Elasticsearch などなど
とメインで使うものを大規模開発・データ処理系に思考を大きく寄せて、
これまでのいろんな考えを結合させて自分自身の今後あつかう技術を大きく変えた

あとHackのライブラリ作りに集中してた

1月

HHVM/HackのHTTPに関するフレームワークをちょこちょこ作り始めだしたのがこの時期

github.com

腰のヘルニアで入院してからの仕事復帰を果たした。

2月

Developers Summit 2018

Apache Kafkaによるスケーラブルアプリケーション開発 ということで、
2017年のペチコンで話したものをもう少し導入のための知識に寄せて話しました。

デブサミ2018 Apache Kafkaの話 - Speaker Deck

第123回 PHP勉強会@東京

最近のHHVM/Hackという話をした

Laravel JP Conference2019のはじまり

何を思ったのか忘れましたが、ここからLaravel JP Conference2019開催になりました。
開催に向けて色々協力していただいた皆さん、
スタッフに応募してくれてくださった皆さん、
スポンサー企業の皆様、
本当にありがとうございます。

3月

Laravel Meetup Tokyo Vol.10

10回目をやっと開催。。。 PHPerKaigi2018と続いてPHP週間みたいな感じで開催。
すごい盛り上がった回

PHPerKaigi2018

Hackで作るマイクロフレームワーク という話をしました。
Hackの楽しさに気付いて実際に導入したりして2~3年 ずっと話したかったHackのCFPがやっと通って話せた・・・。

Hackで作るマイクロフレームワーク - Speaker Deck

2019年も楽しみです

4月

HHVM/Hack勉強会 Vol.1

PHPerKaigiでも話したし、話し続けていこう!と思って開催
個人的にHackで大好きなShapeの話をしました。
HHVM/Hack ShapeとTypeAssertで堅実なアプリケーション - Speaker Deck

Apache Kafka Meetup Japan特装版@Yahoo! JAPAN

PHPとKafkaの話をした。

Apache Kafka with PHP App - Speaker Deck

Kafka自体の話や事例を聞き自分たちはKafkaの想定の範囲内の用途なんだ、と認識
と同時に設計がミスっていないことを再確認できてよかった・・ 下記の二つの話を聞けたのはよかった

  • "The evolution of Apache Kafka" Mr. Jun Rao
  • ”For Multitenancy; Kafka clusters for everyone at LINE”Yuto KAWAMURA

5月

インタビュー記事が公開されました。
インタビューは当然ジャージ www.rstone-jp.com

6月

PHPカンファレンス福岡2018

Event Sourcing, CQRS For PHP という話をしました。 Event Sourcing,CQRS For PHP Application - Speaker Deck

2017年から取り組んでいるデータ処理系の観点を交えながら、
DDDからの視点ではなくて、データ処理系に近い内容
Ask The Speakerにたくさん来ていただいて楽しかった・・!

7月

発表系の活動などは特にしなかった。が、
アイスタイルのCTOになったのがこの月。
技術面はこれまでも横断的に関わることが多かったんですが、
その技術を推進するための成長をどうやってするべきか、
みたいなマネージメント的な方面からも入り出した時期。
頑張ろう

今年はPHPカンファレンス関西に行けなかった。

8月

この辺りから土日はHackだったりAWS周りの開発をめっちゃする時期に突入し、
イベント登壇後のブログとかを書かなくなり、とにかく開発ばっかり

HHVM/Hack勉強会 Vol.2 初心者特集(予)

資料どっかにいってしまった・・・ Hackの始めた方、という話をした。

9月

builderscon2018

Hackの話をした
HHVM/Hackで得る問題解決力 / hhvm-hack-problem-solving - Speaker Deck

アーキテクチャ ディスカッション Vol.1

発表資料等はなく、アーキテクチャに関して質問したりテーマに沿っていろんな方と話す、
というのをどうしてもやりたくて開催

当日の様子は以下を参考にしてください。
shinkufencer.hateblo.jp

qiita.com

alexander.achanblog.mydns.jp

これに関していろんな方面の方々に来ていただいて、
参加した方々には是非また参加したいとフィードバックがあったり、
いろんな話ができたので来年も数回やりたいと思ってます。

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応 本発売

Laravelに関する書籍で自分が執筆した書籍はこれで3冊目
今までのPHP系の書籍になかった内容にしよう、というのと
現場で使えそうなユースケースを交えて、という著者みなさんの思いが詰まった一冊。
執筆期間であろう月日の流れを見ると、
なかなかのハードスケジュールだった・・。

第130回 PHP勉強会@東京

Laravel JP Conferenceの宣伝のみ

10月

明日の開発カンファレンス 2018秋

大きなデータと戦う技術 という話をした 大きなデータと戦う技術 / fighting-large-data - Speaker Deck

アプリケーションの規模に応じて考え方や取り組みを変えていきましょう!という話
パネルディスカッションに参加せていただいて、
組織的な話だったり採用の話だったり、CTOっぽい話をした気がする・・。

11月

AWS Dev Day Tokyo 2018

マイクロサービス周辺技術のトラックで
Event Sourcing+CQRS を活用するスケーラブルアプリケーション開発 という話をした。

PHPカンファレンス福岡で話したテーマを元に、
AWSのサービスを交えたり、PHP要素をなくし、
マイクロサービスアーキテクチャを実践する上で、gRPCなど目に見えるものの裏側のデータの扱いの話がメイン
なんだかんだAWSのイベントに登壇する、というだけでめちゃくちゃ緊張した。
登壇の方々は本当に第一人者といってもいいような方々ばかりで、今までで一番緊張したイベント。
他の方々とgRPC関連の話をしていたところ、ふと思ったのは自分はほとんどAvroとThriftしか使ってないなと思った頃
このあとからgRPC使った画像処理系のAPIとかをAkka HTTPで作りはじめる

12月

PHPカンファレンス2018

PHPとApache Sparkで始めるデータ解析処理 という話をした。
データ処理系の話と機械学習とのつなぎこみとか、実践に近い話の触りだけ。
本当はHackの話をしたかった

Hack and HHVM Advent Calendar 2018

qiita.com

可能な限り書いた。
PHPから大きく分離し始めている今を伝えるのと、
Hackの言語としての魅力を伝えようとしたもの。
たいへんだった

来年

来年は1月から北海道に行ったり仙台に行ったり、
いろんなところに行くので、色んな体験だったりができそうで今から非常に楽しみ。 あとはLaravel JP Conference2019!

conference2019.laravel.jp

LaravelやPHPユーザーは是非お越しください!
いろんなところでも言っていますが、
2回目はやりません。今回のみです。

それとは別に来年はOSS周りのものをリリースする!

  • Apache Kafka PHP製Topic管理、KSQLツール
  • Hackフレームワーク バージョンアップ
  • Hack専用ライブラリ Logger、Elasticsearch周り
  • Hackエクステンション

来年も頑張りましょう

PHPカンファレンス2018でApache Sparkの話をしました #phocon

2018年のペチコンも楽しかった!

f:id:ytakezawa:20181230010118j:plain

もう二週間前の話ですが、
PHPとApache Sparkで始めるデータ解析処理 という話をしました。

speakerdeck.com

現在公開されている動画はこちら
youtu.be *分割されたものが公開されるらしい

アプリケーションを成長させるためには、  
アプリケーションの開発だけではなく、ログデータや、  
様々なメタデータを的確に利用できるようにすることが重要です。  
このセッションではこれらを叶えるためにApache Sparkによるデータ解析処理と、  
ビッグデータ対応のデータベース、  
PHPアプリケーションを組み合わせてアプリケーションをグロースさせるヒントの実装の考え方をお話しします。

トーク補足

Apache Sparkそのものについては、
たくさんの書籍や、ネット上の記事も多くありますのでその詳細は話しませんでした。
(日本国内の記事よりも海外の記事の方がおすすめです)

Apache Sparkを活用するには、まず活用のポイントを知っておかなければなりません。
小さなアプリケーションだったり、
アプリケーションからアドホックに叩きまくりたい、というケースで導入すると
ミスマッチにもなりますし、
自分たち自身で分散システムの保守・運用ができる体制や知識がないと
デメリットとなりますので、その辺りに軽く触れました。
といいつつもWebアプリケーション系に相性もいいSpark SQLを中心に、
Event SourcingとCQRSのランタイムと同時にシステムを支える考え方を交えて紹介しました。

f:id:ytakezawa:20181230012926p:plain

こうした考え方とミドルウェアの組み合わせは、
大きなアプリケーションだったり、リアルタイム性が重要なアプリケーションや、
マイクロサービスアーキテクチャ化に取り入れることもできます。
データ処理アーキテクチャのLambdaアーキテクチャやKappaアーキテクチャでは、
この処理フローを元に様々な処理を連携させていきます。
現代のアーキテクチャで導入を検討するケースも多いパターンかもしれません。
が、やはり事前にその知識が必要であったりするため、セッション自体は少し難しかったかもしれません。

後半はALS (Alternative Least Squares・交互最小二乗法)を用いたレコメンデーションの作り方について、
IBMで公開されているものを元に処理の流れと実装内容を解説しました。

github.com

実際の開発に導入する場合もこれを元に拡張していくといいかもしれません。
自分はSpark MLlibとElasticsearchとHadoopの組み合わせで、
PythonではなくScalaで実装して利用しています。
これは使えそうだ!と思ったら是非アプリケーションに導入してみてください。

アプリケーション開発をしていると、多くは実装だったり、実装のための設計だったり、
そのためのチームでのMTGだったりと、通常その辺りに時間を多く使われると思います。
が、実装や設計といったものと切り離せないデータベースや、
データ処理そのものの情報を吸収するのは難しく
枯れているからこそこれまでの情報と知識を使いがちになります。

が、サービスの規模が大きくなったり、想定外の成長を遂げるアプリケーションを支えるには
技術面における柔軟性と、状態変化に強くするための知識アップデートが必要になることも多いです。

そんな現代において、コンテンツのレコメンデーションなどに代表される機械学習や、
それを作るための大規模なデータとWebアプリケーションを結びつけるものの具体的なイメージがあれば、
アプリケーション開発へのヒントだったり、ログデータの重要性が見えてきたり、
開発者から行えるサービスへのアプローチの面白さ、みたいなのを知るきっかけになれば、
と思います。

スポンサーとしても参加

毎年登壇はしていたんですが、
今回はせっかくなのでスポンサーとしても参加しました。
会社のみなさんにも協力いただいて(自分はほとんど丸投げだった・・・)
たくさんの方がスポンサーブースに来ていただきました。
感謝

f:id:ytakezawa:20181230010136j:plain
アイスタイルブース

Laravelクイズ的なものを実はQRコードで配布していましたが、
結構ちゃんと考えないといけない内容だった為、あまり遊ばれませんでした
Laravel JP Conference2019でもやろうかな
そんな逮捕用のパネルで遊んでくれた方々。

f:id:ytakezawa:20181230010044j:plainf:id:ytakezawa:20181230010104j:plain

サイン会の様子

ありがたいことに今回は、
今年発売された PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応 のサイン会がありました。
サイン会で完売!!!
多くの方に参加していただきありがとうございました!

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

最後にLaravel JP Conderence2019

ペチコンでも最後に宣伝させていただきましたが、
来年開催される Laravel JP Conference2019の参加申し込みが始まっております!

conference2019.laravel.jp

タイムテーブル自体ももうすぐ公開されますが、
豪華な国内ゲストを迎えてLaravelを軸としたセッションをお届けします。
ゲストの紹介、タイムテーブルの公開は、公式Twitterアカウントをフォローしてチェックしてみてください。

twitter.com

来年は1月の仙台、Hackの話をさせていただきます!
楽しみ!!!!!

Kakfa ConnectでSinkが動かないときの確認メモ

Apache KafkaからHadoopのHDFS転送で、
うまく転送できないものがあり、忘れないようにするためのメモ

アプリケーション自体はいわゆるWebアクセス系のトラッキング

  • メタデータを付与したログデータをFluentdで収集
  • Apache Kafkaのtopicに格納
  • Kafka StreamsでTopicのデータ内で完結する前処理後、他topicへ転送
  • Kafka Streams処理後のデータをHDFSへ転送

connect-distributed

distributedで動かすのは、connect-distributedコマンドで直接実行するか、
または

$ systemctl start confluent-kafka-connect

通常のアプリケーションから送信されるときはJSONにして送ることが多く、
そのままの設定でconnectへ登録しました(Webアプリケーション側にはAvro導入していない)

hdfsへのsink connect例は下記

name=hdfs-sink
connector.class=io.confluent.connect.hdfs.HdfsSinkConnector
tasks.max=1
topics=streamed.purchase_promotion
hdfs.url=hdfs://hdfshostname/path
flush.size=3

key.converter=org.apache.kafka.connect.json.JsonConverter
key.converter.schemas.enable=false
 
value.converter=org.apache.kafka.connect.json.JsonConverter
value.converter.schemas.enable=false
# 省略
format.class=io.confluent.connect.hdfs.json.JsonFormat

正常に登録されたかどうかは、RESTで問い合わせることで判定ができます。

$ curl localhost:8083/connectors
["hdfs-sink"]

対象のconnectorだけを確認する場合は、
curl localhost:8083/connectors/connector-name で問い合わせます。
Fluentdからの転送とKafka Streamsの動作を確認して、
Connectだけが動かない・・・。

確認するところ

connectorのtask状況もRESTで問い合わせて確認できます。

curl localhost:8083/connectors/connector-name/tasks/0/status | jq
{
  "state": "FAILED",
  "trace": "org.apache.kafka.connect.errors.ConnectException: Tolerance exceeded in error handler\n\tat ....(省略",
}

何かエラーが出ているので、その内容を探します。

jacksonでパースエラー発生

Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'hoge': was expecting

topicへ挿入されるメッセージ自体はconsumeコマンドで確認できます。

$ kafka-console-consumer --from-beginning --topic topic.name --bootstrap-server hostname:9092
{"referer":null,"event": "blog"}

JSONで挿入されているので問題はありませんでした。
このデータはいわゆるValueなため、Keyも確認します。
keyを取得する場合は、property print.key=true を指定します。

$ kafka-console-consumer --property print.key=true --from-beginning --topic topic.name --bootstrap-server hostname:9092
hoge.stdout  {"referer":null,"event": "blog"}

どう云ったキーで送信されているかがわかります。
kafka-connectのkey.converterをStringに対応させるには以下のConverterを指定

key.converter=org.apache.kafka.connect.storage.StringConverter

再度登録し直して、転送がスタートされました。

Laravel JP Conference 2019開催について

Laravel JP Conference2019

f:id:ytakezawa:20181008205256p:plain

Twitterなどでももう知っている方も多いと思いますが、

2019/02/16 にLaravel JP Conference2019を開催します。

今回自分が実行委員長を努めさせていただきます。

概要について

概要についてはこちらをご覧ください

Laravelを軸としたカンファレンスになりますが、基本的にはPHP関連を扱うカンファレンスと同等の内容でお届けします。
当日は3会場あり、それぞれの会場は以下のカテゴリで分けて同時に開催されます。

  • Laravelメインのセッション
  • PHP関連システムに関連するセッション・
  • その他企画・実装相談会・ワークショップなど

スポンサーについて

ブロンズとドリンクスポンサー以外は売り切れとなっていますが、
まだ枠はございます。
他にもこういう協力ができるかも、という企業様などがありましたら竹澤までご連絡いただけると幸いです。

twitter.com

Twitterについて

公式のアカウントは以下のアカウントです。

twitter.com

スピーカー募集やチケット販売などは公式アカウントからアナウンスさせていただきますので、
ぜひフォローしてください!

スピーカー募集について

募集開始については上記のアカウントからアナウンスとなりますが、
以下の内容でトークできるぜ!という方は是非お願いします!

  • Laravelのノウハウ・Laravelを実際にこうして使っているよ (商用投入系が望ましい)
  • PHPアプリケーション設計技法について (Laravel問わず)
  • アプリケーションを支えるデータベース設計・ミドルウェア周りについて
  • ワークショップをやりたい

スピーカー応募は10月中旬頃から開始予定です。

チケット販売について

チケットについてはカンファレンス本編のチケット・懇親会チケットと分けて販売されます。
こちらも今月中旬以降に開始予定です。

スタッフ募集について

当日スタッフなどの募集は後日アナウンスされます。
コアスタッフについては現在募集枠として用意している訳ではありませんが、
是非やりたい!という方はご連絡ください!

現時点では一度限りのカンファレンスとしており、2020年は開催する予定はありません。

「PHPフレームワーク Laravel Webアプリケーション開発」執筆しました

新しいLaravel本

今回下記の書籍を執筆しました

PHPフレームワーク Laravel Webアプリケーション開発

Laravelリファレンス の延長という訳ではなく、
機能解説ではなく、実際に開発を行う上でヒントになるような内容になっていると思います。
Laravelに慣れた次のステップに進められるような内容だったり、
バージョン問わず導入できる内容を心がけました。

今回担当したのは、3章、4章後半、6章、7章と10章です。

全体を通してドメイン駆動設計などは盛り込まず、責務分割や、API設計などの視点がいくつか入っています。
いくつかとりあげてみると・・・

3章

幅を広げすぎないようにMVCとADRのみに焦点を絞って、簡単な解説などがあります。
どちらがいい、という話ではなく開発チームの体制や、長期運用するかどうかなどで大きく変わります。
この先の知識や学習するにはどうしたらいいんだろう、
公式ドキュメントだけの使い方だけではない選択肢の一つとして読んでみると楽しめると思います。

4章後半

レスポンスとミドルウェアが主ですが、
少し前に取り上げられることも多かったSSE(ストリームを使ったレスポンス)だったり、
APIの REST Level 3 / HALの導入方法だったり、
これからのアプリケーション開発に導入することができる内容になっていると思います。

7章

EventとQueueの基本に触れながら、データ分析処理などでも用いることが多いCQRSライクな方法に発展します。
大きなアプリケーションではメッセージングをApache KafkaやAmazon SQS、Kinesis Data Firehose、
RabbitMQなどに差し替えてみてください。

個人的なオススメは後半のテスト関連の9章と11章です!

本書は必ずしも初めてLaravelを使う、という方には難しい内容かもしれませんが、
多くの内容はLaravelに限らず導入できる内容になっています。

アプリケーション開発のヒントとして役立てていただければ幸いです。

blog.shin1x1.com

おまけ
f:id:ytakezawa:20180930032902j:plain
厚さに注目!

PHPカンファレンス福岡2018で「Event Sourcing, CQRS For PHP」の話をしました

先日開催されたPHPカンファレンス福岡2018

f:id:ytakezawa:20180704013557j:plain

いいカンファレンスを開催しよう!という福岡の皆さんの気持ちがとても良いカンファレンスで、
今年も登壇しつつ、いろんなセッションに参加したり、 いろんな方と技術なトークができて最高でした。

phpcon.fukuoka.jp

トーク内容

今年はタイトルにもある通り、「Event Sourcing, CQRS For PHPという話をしました。
DDDと一緒に紹介されたりすることも多いものですが、
データ処理方面からの話が主の内容でお届けしました。

ビックデータの処理、といえばphp以外の言語で処理することがほとんどですが、
こういったミドルウェアアーキテクチャを組み合わせることで、 目の前にある課題をシンプルに解決していくためのヒントになることも多く、
データ周りが複雑で、どうやったらいいんだろう、という方にも参考になれば幸いです。

この辺りのテーマは一昨年、去年、今年としばらくデータ処理系の開発や、 アーキテクチャを考えることが多く、
そこで実際に導入した方法や苦労したことなどをphpを通して話をしてるシリーズの一つです。

ytake.hateblo.jp

デブサミ2018 [Apache Kafkaによるスケーラブルアプリケーション開発] で登壇してきました #devsumi

トーク終了後、Ask the Speakerでもたくさんの方が質問してきてくれたり、
ミドルウェア周りの悩みをみんなで話したり、今回この話を選択してよかったなぁと思いました。

Twitterなどでも色々反応いただけてありがとうございました。

参加したセッション

skaffold を使って Kubernetes してみたLaravel Queue でスケーラブルなバッチシステムの構築

PHPerのためのよくわかるCPU脆弱性解説DDoS攻撃との終わりなき戦い

PofEAAで読み解くDoctrine2AI(人工知能)に聞いてみた! - クラウドサービスではじめる動画解析/音声変換 -

そして最後の 物理層のこと、時々でいいから、思い出してください。

でした。

DDoS攻撃物理層の話が特に印象に残りました。
また来年のカンファレンスも楽しみにしています!

カンファレンス前後

カンファレンス前後はやっぱりいつものところでphper大集結でした

f:id:ytakezawa:20180704013547j:plain

ラーメン食べたり

f:id:ytakezawa:20180704013601j:plain

懇親会では良いビールがあったり

f:id:ytakezawa:20180704013639j:plain

今回は会社のステッカーを持って行きました

f:id:ytakezawa:20180704013611j:plain

最高!

そして Laravel JPカンファレンス

さらっとここに記載しますが、
来年Laravel JPカンファレンスを開催します。
東京で開催しますが、2トラック進行とLaravel相談会を用意する予定です。
Laravelづくしのトラックと、アプリケーション開発・設計におけるトラックを考えていますが、
まだわかりません。

こちらはこちらでお楽しみに・・!

HHVM/Hack マイクロフレームワークにCache追加

ytake.hateblo.jp

以前から作ってたフレームワークで、
都度Cache組み込むのが面倒なのと、HackでPSRに準拠する必要もないだろうということで、
JavaEhcacheっぽい名前(名前だけ) のものを作り、
マイクロフレームワークに組み込みました。

github.com

HHVM/Hackにはmemcachedとredisのエクステンションが含まれているので、
なにもしなくても使えるのが楽で良いです。

後日 Mcrouter 対応ドライバも追加してリリースする予定です。

簡単な使い方

使いたいキャッシュドライバーをCacheManagerで指定すると、
そのキャッシュドライバが利用できるようにしています。

が、ドライバー単体でも利用できます

<?hh
use Nazg\HCache\Element;
use Nazg\HCache\CacheManager;

$manager = new CacheManager();
$cache = $manager->createCache('memcached');
$mc = new \Memcached('mc');
$mc->addServers([['127.0.0.1', 11211]]);
$cache->setMemcached($mc);
$cache->createCache('map')?->save('cache', new Element('testing', 0));

独自ドライバ追加方法

Nazg\HCache\CacheProvider クラスを継承していればなんでも使えます
ドライバ実装は下記の通りです。

<?hh // strict

use Nazg\HCache\CacheProvider;

class NullCache extends CacheProvider {
  <<__Override>>
  public function fetch(string $id): mixed {
    return;
  }
  <<__Override>>
  public function contains(string $id): bool {
    return false;
  }
  <<__Override>>
  public function save(string $id, Element $element): bool {
    return true;
  }
  <<__Override>>
  public function delete(string $id): bool {
    return true;
  }
  <<__Override>>
  public function flushAll(): bool {
    return true;
  }
}

上記の登録後は、以下の通りに記述するだけで利用できます。
簡単

<?hh

use Nazg\HCache\Element;
use Nazg\HCache\CacheManager;

$manager = new CacheManager();
$manager->addCache('null', () ==> new NullCache());
$manager->createCache('null'); // return NullCache

Hackで動的メソッドコール実装するヒント

Hackでは基本的に動的メソッドはtype checkerでエラーになりますが、
type checkerが理解できるように記述すればOKです。

CacheManagerは下記の実装になっています

<?hh

  protected Map<string, classname<CacheProvider>> $cache = Map {
    'apc' => \Nazg\HCache\Driver\ApcCache::class,
    'void' => \Nazg\HCache\Driver\VoidCache::class,
    'map' => \Nazg\HCache\Driver\MapCache::class,
    'file' => \Nazg\HCache\Driver\FileSystemCache::class,
    'memcached' => \Nazg\HCache\Driver\MemcachedCache::class,
    'redis' => \Nazg\HCache\Driver\RedisCache::class,
  };

  protected Map<string, (function():CacheProvider)> $userCache = Map{};

  public function createCache(string $namedCache): ?CacheProvider {
    if($this->cache->contains($namedCache)) {
      $cache = $this->cache->at($namedCache);
      return new $cache();
    }
    if($this->userCache->contains($namedCache)) {
      $cache = $this->userCache->at($namedCache);
      return $cache();
    }
    return null;
  }

CacheProviderでは実は下記のようになっており、継承に制約をつけています。

<?hh // strict

namespace Nazg\HCache;

use Nazg\HCache\{Cacheable, FlushableCache};

<<__ConsistentConstruct>>
abstract class CacheProvider implements Cacheable, FlushableCache {

}

コンストラクタに何かを付け加えることはできませんので、
インスタンス生成方法が統一されていますので、type checkerが 通ってよし と判断しています。

フレームワークへの組み込み

Containerにインスタンス生成方法等を登録します。

設定値はクラスでは関与せず、外から与えるものとしてshapeで縛っています。

<?hh

abstract class CacheServiceModule extends ServiceModule {
  protected Driver $defaultDriver = Driver::File;
  <<__Override>>
  public function provide(FactoryContainer $container): void {
    $container->set(
      CacheManager::class,
      $container ==> new CacheManager(),
      \Ytake\HHContainer\Scope::SINGLETON,
    );
    $container->set(
      CacheProvider::class,
      $container ==> {
        $manager = $container->get(CacheManager::class);
        if($manager instanceof CacheManager) {
          return $this->detectCacheProvider(
            $manager->createCache($this->defaultDriver),
            $this->cacheConfigure($container)
          );
        }
        throw new \RuntimeException("Failed to resolve " . CacheProvider::class);
      },
      \Ytake\HHContainer\Scope::SINGLETON,
    );
  }

  abstract protected function cacheConfigure(
    FactoryContainer $container
  ): CacheConfiguration;

  protected function detectCacheProvider(
    ?CacheProvider $provider, 
    CacheConfiguration $cacheConfigure
  ): CacheProvider {
    invariant($provider instanceof CacheProvider, "provider type error");
    if($this->defaultDriver === Driver::File) {
      if($provider instanceof FileSystemCache) {
        $dir = $cacheConfigure->getFileSystemDir();
        if(!is_null($dir)) {
          $provider->setDirectory($dir);
        }
      }
    }
    if($this->defaultDriver === Driver::Memcached) {
      if($provider instanceof MemcachedCache) {
        $m = $cacheConfigure->getMemcached();
        if(!is_null($m)) {
          $provider->setMemcached($m);
        }
      }
    }
    if($this->defaultDriver === Driver::Redis) {
      if($provider instanceof RedisCache) {
        $r = $cacheConfigure->getRedis();
        if(!is_null($r)) {
          $provider->setRedis($r);
        }
      }
    }
    return $provider;
  }
}

このクラスを継承し、利用者がどこから設定値を持ってくるかを指定すれば適切に起動します。
設定値のshapeは下記の通りです。

<?hh // strict

namespace Nazg\Cache;

type MemcachedServer = shape(
  'host' => string,
  'port' => int,
  ?'weight' => int,
);
type FileSystemConfig = shape(
  'cacheStoreDir' => string
);
type MemcachedConfig = shape(
  'servers' => ImmVector<MemcachedServer>,
  ?'persistentId' => string,
);
type RedisConfig = shape(
  'host' => string,
  ?'port' => int,
  ?'password' => string,
  ?'prefix' => string,
  ?'readTimeout' => float,
  ?'persistent' => bool,
  ?'database' => int
);

Skeletonへの組み込み

フレームワークのSkeletonを使えばこの辺りはデフォルトで対応しています。

github.com

設定値は下記で用意しています。(cache.global.php)

<?hh

use Nazg\Cache\Driver;
return [
  \Nazg\Foundation\Service::CACHE => [
    /*
     * Supported "apc", "map", "file", "memcached", "redis", "void"
     * Driver::Apc, Driver::Map, Driver::File, Driver::Memcached, Driver::Redis, Driver::void
     */
    'driver' => Driver::Memcached,
    'drivers' => [
      Driver::File => shape(
        'cacheStoreDir' => __DIR__ . '/../storages/cache/',
      ),
      Driver::Memcached => shape(
        'servers' => ImmVector{
          shape(
            'host' => '127.0.0.1',
            'port' => 11211,
            'weight' => 100,
          ),
        },
        // 'persistentId' => 'rename',
      ),
      Driver::Redis => shape(
        'host' => '127.0.0.1',
        'port' => 6379,
        'database' => 0,
        // 'password' => '', // optional
        // 'prefix' => '', // optional
        // 'readTimeout' => 1, // optional
        // 'persistent' => false, // optional
      ),
    ],
  ],
];