はじめに
PHPにはクラスやメソッドの構造を取得できるリフレクションという機能があります。PHPフレームワークのFlowではPHP標準のリフレクション処理を用いた独自のリフレクション処理が定義されており、様々な機能を追加で使えるようになっています。
そんなFlowのリフレクション処理ですが、公式ドキュメントを読んでいると以下のような記載がありました。
Flow provides a powerful extension to PHP’s own basic reflection functionality, not only adding more capabilities, but also speeding up reflection massively.
"speeding up reflection massively..."
どうやら、FlowのリフレクションはPHP標準のものよりも速いとのこと。
FlowもPHP標準のリフレクションを利用しているのになぜ差が出るのか気になったので調べてみました。
TL;DR
- Flowは実行前にコンパイルと呼ばれる処理を行っている(Javaなどのコンパイルとは全くの別物)
- コンパイル時にリフレクションデータをキャッシュしているため、PHP標準のものよりも高速になる
- キャッシュしたリフレクションデータはフレームワーク自体で使用しているため、全体的なパフォーマンスが向上する
そもそもリフレクションとは
リフレクションとは、クラスやメソッドなどの構造をデータとして扱うことができる機能です。
この機能を使うと以下のような情報を取得することができます。
- クラスの属してる名前空間
- クラスに定義されているメソッドの一覧
- クラスが利用しているtraitの一覧
- クラスの継承元やインターフェース
などなど。
リフレクションについてはこちらの記事でまとめたので良ければご覧ください。
Flowのリフレクション処理
続いてFlowのリフレクションについてです。
FlowではReflectionService
というリフレクション機能を提供するクラスが標準で備わっています。このクラスにはPHP標準のリフレクションを用いた独自のメソッドが多数書かれています。
実際に使い方を見てみましょう。
getClassNamesByAnnotation
という、特定のアノテーションが付与されたクラス名の一覧を取得するというメソッドを試してみます。
取得したクラス名はログに出力してみました。
<?php
namespace Neos\Welcome\Service;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Reflection\ReflectionService;
use Psr\Log\LoggerInterface;
/**
* @Flow\Scope("singleton")
*/
class TestReflectionService {
/**
* @Flow\Inject
* @var ReflectionService
*/
protected $reflectionService;
/**
* @var LoggerInterface A logger implementation
*/
protected $logger;
public function injectLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
/**
* アノテーションを使用してクラス名を取得する
*/
public function outputReflectionClass(): void
{
$classNames = $this->reflectionService->getClassNamesByAnnotation('Neos\Flow\Annotations\Entity');
$this->logger->info('------start output reflectionClass------');
foreach ($classNames as $className) {
$this->logger->info($className);
}
$this->logger->info('------end output reflectionClass------');
}
}
こんな感じで、Neos\Flow\Annotations\Entity
が付与されてるクラスの全量が取得できました。
24-01-27 03:10:42 15712 DEBUG ------start output reflectionClass------
24-01-27 03:10:42 15712 DEBUG Neos\Flow\Mvc\Routing\ObjectPathMapping
24-01-27 03:10:42 15712 DEBUG Neos\Flow\ResourceManagement\PersistentResource
24-01-27 03:10:42 15712 DEBUG Neos\Flow\Security\Account
24-01-27 03:10:42 15712 DEBUG Neos\Welcome\Domain\Model\Image
24-01-27 03:10:42 15712 DEBUG Neos\Welcome\Domain\Model\Item
24-01-27 03:10:42 15712 DEBUG Neos\Welcome\Domain\Model\OrderDetail
24-01-27 03:10:42 15712 DEBUG Neos\Welcome\Domain\Model\Users
24-01-27 03:10:42 15712 DEBUG ------end output reflectionClass------
Flowのリフレクション処理はなぜ速いのか
さて、本題です。
なぜFlowのリフレクション処理はPHP標準のものよりも速いのでしょうか?
結論を言うと答えはキャッシュです。
Flowは起動時にコンパイルと呼ばれる処理が走ります(※これはJavaなどのコンパイル言語で行われる一般的なコンパイルとは全く別物です)。
このコンパイルの際にFlowではリフレクション情報をキャッシュしておくことで高速なリフレクション処理が実行できるというわけです。
リフレクションはFW内部の処理で使われています。そのため、この「Reflectionをキャッシュする」という仕組みはFlowの全体的なパフォーマンス向上にもつながっているというわけですね。
終わりに
タネが分かると「まぁそうだよな...」って感じですよね。
とはいえ、FWの内部処理を細かく調べていく過程は面白かったし力になりました。
リフレクションの具体的な使用例なんかも調べてみたいと思っているので、また書こうと思います。
ここまで読んでいただきありがとうございました!
参考