こんにちは。やまゆです。
2022年9月24日(土) ~ 2022年9月25日(日) に東京で開催された PHP Conference 2022 に参加してきたので、レポートしたいと思います!
2019年に参加してから実に三年ぶりのテックイベントへの参加になります。前回参加時の記事は下記になります。
PHPカンファレンス北海道 2019 に Slim FW と PSR エコシステムの話で登壇します!
東京で開かれる PHP Conference Japan への参加は初めてだったのと、そもそもオフラインイベントがここ最近なかったので非常に新鮮な体験が出来ました!お会いして会話してくださった方々、スポンサーブースの方々、運営スタッフの方々、その他今回のイベントに関係した全ての皆様に感謝しています!
経緯
「へ~、今年はペチコン東京あるのか。じゃあプロポーザル※送ってみよ~」と、プロポーザルの締め切り前日にざっくり書いたものがなんと採択されたので「おっ、これは行かねば!」となったのがきっかけです。
※プロポーザルとは、「こういう話をしたい」という提案をして、その中から多くの人が聞いてみたいと感じられる面白そうなものを運営が選ぶ、というトーク採択のスタイルです。
カンファレンス参加支援制度
弊社インフィニットループは 「技術の会社」 として、エンジニアの技術力を向上させたり、外部へPRしたりすることがミッションの一つとなっています。そのために様々な制度が存在するのですが、その中の一つに 「カンファレンス参加支援制度」 というものがあります。
これは、参加する社員自身の成長を促したり、社内で共有することで社内全体の知見を増やしたり、登壇することで社外へのアピールをしたりすることを目的として作られているもので、札幌・仙台以外で開催されるカンファレンス等イベントへの参加に必要な旅費を会社が援助してくれるというものになります。今回はこの制度を利用させてもらいました。
見たセッションの感想
1日目の午後からの参加となりました(午前中は飛行機に乗っていました)。
フィーチャートグルを使って素早く価値を検証する 早く安全に失敗し学ぶために
なんとなく「フィーチャートグルって良い機能だよな~」とふんわり思っていたのですが、このお話を聞くと「何故良いのか」の解像度がグっと上がったのを感じました。自分がこの機能に求めていたのが 「不確実性を下げる」 効果だと気づきました。
「欲しいと思った機能の量」は、「実装可能な機能の量」より確実に多くなるので、どれかの実装は諦める必要があります。では「どの機能を実装するのか」という部分を決めなければなりません。その時に「本当に価値があるかはわからない(=不確実な領域)けどしっかり実装してリリースしよう」としてしまうと、いざユーザーの元へ届けられたとしても使われなかった、という可能性があります。しかしリリースしてしまった以上、その機能を削除するには余計な労力が必要です。要するに「負債」となってしまいます。
そこで、「仮の最小実装を一部のユーザーにだけ公開して、そこで 「確実に価値があることを検証」 してから、本実装として取り込む」という選択肢が取れます。検証のサイクルが短くなるほど無駄は減っていくので、効率良くユーザーの体験を向上させる実装に集中することが出来るようになります。このあたりがキーになるかなと。
スケールアウト可能なマネージドデータベースサービスTiDB Cloudのご紹介
弊社は LAMP(Linux/Apache/MySQL/PHP) をメインスタックとしています。そこで MySQL プロトコルを使えて、大規模なスケーリングが可能な TiDB(タイディービー) は以前から興味を持っていました。
MySQL(Aurora) をスケールするには、いくつかの手段があります。
- インスタンスのスペックを上げる
- レプリケーションにより読み込み DB を増やす
- リレーションしないテーブルを別の DB に移動する(垂直シャーディング)
- ID を持って DB を振り分ける(水平シャーディング)
それらを行うことで、高負荷にも耐えられるクラスターを構成することが可能です。しかし、もちろん問題もあります。
- インスタンススペックには限界がある
- レプリケーションは遅延が発生する場合があるため、書き込み直後のデータが取得出来ない場合がある
- シャーディングはアプリケーション構造の複雑性を上げる
それらを解決するために様々なアプローチが取られていますが、 TiDB を使えば、アプリケーション構造の複雑性の増加を抑えつつ、またさらに大規模なスケーリングを可能としてくれるようです。
TiDB は SQL の解析(パース/実行計画/最適化等)を行うインスタンスと、データの保持を行う TiKV というインスタンスが分けられています。 SQL の解析には時間がかかる場合があるため、ここだけをスケールしたい場合も考えられます。データの保持と責務を分離しているのが特徴的だと感じました。
また、 SQL でデータ解析(KPI集計など)を行いたいという要求がありますが、その際行指向のデータでは思ったほど速度が出ないことが多いです。そこで、 TiDB には TiKV だけでなく TiFlash という列指向のストアも用意されています。データ解析の部分で自動的にそのデータを TiKV に置くべきか、 TiFlash に置くべきかを判断し、適切に振り分けてくれます。 MySQL 単体だとデータ解析は難しく、別のデータストアを利用することも多いと思いますが、 TiDB では別のものを用意せずに要求を満たせる可能性があります。
もちろん TiDB が万能というわけではありません。例えば TiDB クラスターの最小インスタンス数は 8 台です。内部開発用などで用意するにはインスタンスコスト・メンテナンスコストが高いですね。 MySQL 互換をうたっていますが、トリガーや外部キー制約などサポートされていない機能もあるところは注意が必要です。
インスタンスの管理を不要とするための SaaS として TiDB Cloud が提供されています。
PHPで学ぶシステム設計 依存関係のコントロール編
アプリケーション設計において依存関係をコントロールすることは非常に重要です。依存が多ければ多いほど、依存されているコードを書き換えるコストが増加しています。 Util や Common のようなものを作ってしまうと、負債になりがちになる原因の一つです。適切に依存関係を管理することで、改修コストを下げることは効果的なアプローチです。
「依存関係逆転の原則」「開放・閉鎖の原則」 というものがあります。例えば、 Controller 具象クラスが Model 具象クラスに依存している場合、 Model を変更すると Controller が動かなくなってしまうというリスクが生まれます。 Controller は Model クラスの実装が欲しいわけではなく、単純に「ユーザー名を更新したい」だけであるとすれば、 function save(int $userId, string $name): void
といったメソッドさえあれば良いわけです。では、そのメソッドを interface として定義し、 Controller はその interface に依存すれば良いわけですね。
私がこの話で一番印象に残ったのは 「変更の主導権を誰が握っているか」 という観点です。つまり、特定の機能を修正したい場合、どのクラス・パッケージ・人間・部署・会社が変更を承認できるのか、ということかなと思っています。 Service A に依存した他の Service が増えれば増えるほど、変更しづらくなってしまいます。これはソースコードだけでなく、依存する 「内部の別サービス」 や 「外部サービス」 や 「人」 や 「企業」 にも同じことが言えます。
そこで、その変更コストを最小限に保つため、我々が基本的に利用しているのは Application Programming Interface つまり API ですね。 API という抽象に依存することで、実装を変更しても、その API を利用している他のサービスが一緒に修正をする必要がなくなります。
主導権という単語と、「依存はクラスだけでなく人や企業でも同じことが言える」という所が今回の学びでした。
PHP メモリ管理術
PHP エンジニアは Allowed memory size of XXX bytes exhausted
というエラーを見たことがあることが多いのではないでしょうか。これは「PHP が利用可能なメモリをオーバーした」エラーです。利用可能なメモリ量は memory_limit=256M
という設定で変更することが可能です。実は PHP はデフォルトでは常に 2 MB のメモリを確保しているとのことです。 ini_set('memory_limit', '1M');
とすると起動しなくなってしまいます。
PHP には参照されている数で解放しても良いかどうかを判断する Garbage Collection が搭載されています。循環参照してしまうとメモリリークが発生しますが、 WeakRef を使えば少し安全になるそうです。
PHP でももちろん、大きなファイルの操作を伴うもの、バッチ処理など大量のレコードを同時に処理するものなどではメモリの管理を適切に行う必要があります。単純に memory_limit を増やす暫定対処ではなく、何故メモリを食っているのか、どうしたらメモリ消費を抑えられるのかを考えることが大切ですね。
いちユーザーが PHP に新機能を追加するまで – Random Extension 5.x
PHP 8.2 には乱数生成に関する新しいオブジェクト指向クラスが実装されます。これは mt_rand()
関数などを置き換えるものとなっています。
$mt19937 = new Random\Engine\Mt19937();
$randomizer = new Random\Randomizer($mt19937);
$foo = $randomizer->nextInt();
若干冗長に見える API ですが、この実装には大きな意味があります。最も大きな課題は 「現行のグローバル関数の挙動は PHP グローバルな変数に依存している」 ことです。
mt_srand(1234);
mt_rand(); // 411284887
mt_srand(1234);
$arr = range(1, 10);
shuffle($arr);
mt_rand(); // 1848274264 <- アレ?
mt_srand(1234);
str_shuffle('bongo');
mt_rand(); // 940013158 <- アレ?
mt_srand
を使うことでシード値を固定し、再現性のある乱数を生成することが出来ますが、これはグローバルな変数に依存しているため、同じロジックを使っている shuffle
などの関数を途中で使ってしまうと、乱数が変わってしまうという挙動を示します。これは直感的ではないですね。
ゲーム開発において乱数はガチャ、ダメージ分散、自動生成データなど様々な要素に利用するため、非常に重要な機能です。その機能を安心して使うことが出来ないとなると、かなり厳しいものがあります。そこで登壇者の企業では PHP による実装や C extension の実装を追加したそうです。さらに「これはコア機能としてあっても良いのではないか」と考え、 PHP に大きな機能を搭載する際に用いられる 「RFC」 を出すことに決めたそうです。
C extension と php-src はまた異なる実装スタイルであることや、 Windows でも動くようにしなければならない、基本的に会話が全て英語なのでコミュニケーションが難しいなど様々な困難があったそうですが、無事に PHP 8.2 で導入されることになりました。諦めずに根気強くアプローチした素晴らしいコントリビューションだと思います。
フレームワークの機能を使わずに標準関数使ったら障害起こした話
私も昔標準関数を使ってインシデントを出したことがあるので、自戒を込めて話を聞きました。今回のターゲットは header
関数。これも mt_rand
と同じくグローバルな変数を使うものですね。フレームワークの機能としてリダイレクトすべきものを、直接 header
関数でおこなってしまったため、想定していない挙動になってしまったとのこと。あるある案件です。
この話で面白かったのはエンバグというよりも、 「障害が起こってから解決するまでのフローがかなり整備されていた」 ことです。人間が関わる以上障害を完全にゼロにすることは出来ません。なので、障害発生時に適切な流れを構築し、早期に解決し、再発防止のために振り返ることが重要です。
障害を起こしたのは誰のコードなのか、という犯人探しは不要ですね。レビュー文化があるのであればレビューした人も当事者ですし、そもそも個人にアプリケーションの障害を押し付けるのはチームとしてバッドアプローチです。しかし当事者はどうしても「やってしまった」とメンタルに来てしまいます。それを最小限に抑えるためのフローが必要です。事前にフローを整備しておくことで、「チーム全体として対応」を行うことが出来るようになり、個人への負担が軽減されます。
そもそもバグを出来るだけ入れないようにする(静的解析やレビューなどのアプローチ)、そしてもしバグが発覚しても落ち着いて対処できるように準備しておくことの重要性を理解できました。
さっぱりPHP 〜 標準関数と文法を極める
PHP の標準関数、多すぎ?案件でした。
PHP には「コア拡張」と呼ばれる、コンパイル時に自動で入ってくれる拡張があり(コア機能なのか拡張機能なのか名前が分かりづらい)、それも含めると 700 個くらいのグローバル関数があるそうです。依存の話もそうですが、グローバルなもので良い物ってあまりないですよね…。今後は Random クラスのように namespaced なオブジェクト指向クラスが増えていくのでしょうか?
SPAセキュリティ超入門
多分知っている中身だとは思いますが、再確認するために受講しました。今回は Single Page Application におけるセキュリティの基本のキでした。
React + Laravel の環境を想定したものでした。例えば routing を間違えて、管理アカウントである必要のあるページに適切な Middleware が割り当てられておらず、一般アカウントでアクセス出来てしまう、とか、リクエストパラメータに userId があり、自分ではない userId を付けてリクエストすると、他人のデータを読み込めてしまう、など超基本的な脆弱性が紹介されていました。<img src=/ onerror=alert(1) />
このようにコールバックに JavaScript を埋めこんで XSS が有効になっていないかチェックするのが効果的、という所が最も実践に使えそうな新しい知識でした。
「いや、そんな基本的なこと起こらんやろ」と思いますが、こういった単純なものも意外とこの世界には存在するらしいです。確かに1行(場合によっては1文)間違えるだけで脆弱性になりうるわけですので、やはりセキュリティは常に強く意識してコーディングする必要がありますね。
導入から 10 年、PHP の trait は滅びるべきなのか ーーその適切な使いどころと弱点、将来について
– プロポーザル
弊社 いがらしさんの話です。
私も昔 trait についての記事を書きましたが、今回は 「基本的に PHP の trait は使わない方が良いだろう」 という内容でした。
PHP trait 実装は、影響範囲が分かりづらく、予期しない挙動をすることがあるため、多重継承の代わりとして多用すると複雑性が一気に上がってしまいます。 継承ではなく合成を使う のがベストプラクティスとのことです。
trait A
{
public function foo(): void
{
// ...
}
}
class X
{
use A;
}
// ↓ better
class A
{
public function foo(): void
{
// ...
}
}
class X
{
public function __construct(private readonly A $a)
{
}
public function foo(): void
{
$this->a->foo();
}
}
例えば、「自動生成による実装と、そこから上書きする手動実装を分離する」という使い方はアリかもしれない、とのことでした。 C# だと Parctial Class でそういったことを実現していますね。
治っていく mbstring 令和時代の文字化け
㋿ ← こういった環境依存文字や、日本語で使われる文字は数えきれないほどあります。それらの変換などを扱う PHP の extension として mbstring というものがあります。mb_strlen
のようなマルチバイトに対応した関数を用意してくれているものですね。
PHP 8.1 ではこの mbstring の実装が「直ってしまった」部分がありました。つまり、間違った実装が修正されてしまい、互換性がなくなってしまった、という話です。 PHP のメインコントリビューターは第一言語が英語であることが多いため、日本語部分での互換性の破壊に気づかなかったとのこと。複数の日本人が声を上げて最終的に「以前の実装を尊重する」ように修正されたそうです。
文字コードの話は非常に複雑で難しいもの(例えば Shift_JIS と言っても色々方言があるとか)で聞いていた時は頭が混乱していたのですが、聞いた後も「さっぱりわからん」でしたw
自分の LT の感想
正直今回自分の LT はあくまでオマケで、メインはコミュニケーションだなと思って参加したんですが、もうちょっとしっかり準備してもよかったな、と若干後悔している所があります。今度何か登壇する時はもうちょっと時間をかけようと思いました。
全体を通して
皆さんのセッションを聞いている間は Twitter で実況していました。
セッションとセッションの時間が長めにとられていたので、その時ブースにいる方や Twitter でしか存じてしなかった方と話すことが出来て、かなり充実していました。会話してくださった方々ありがとうございます。
また、セッションは 「PHP 自体のソースコード読む人が多いな、すごい」 という感想でした。中々ディープなものが多かったです。 PSR やフレームワークなど、アプリケーションエコシステムの話は少なかったイメージです。
ということで、東京に行ってカンファレンス参加も中々楽しいという経験が出来ました。札幌・仙台の会社でもこういう機会を設けてくれるので、 PHP を中心としたテックに興味がある方は是非採用ご応募お待ちしています!