PHP-CS-Fixerはどう動く?「fix」コマンドのエントリーポイントをコードで追う
PHP-CS-Fixerを“使う側”でも“作る側(OSSコントリビュート)”でも、最初に理解しておきたいのが「どこから処理が始まり、どこでFixerが呼ばれるのか」という流れです。
この記事では fix コマンドを題材に、autoload → Console Application → FixCommand → Runner → Fixer のつながりを、実際のコードをもとに解説します。
全体像
[CLI スクリプト php-cs-fixer]
→ vendor/autoload.php(Composer autoload 起動)
→ PhpCsFixer\Console\Application(コマンド登録)
→ PhpCsFixer\Console\Command\FixCommand(設定読込・実行準備)
→ PhpCsFixer\Runner\Runner(ファイル走査・Fixer適用)
→ PhpCsFixer\Fixer\...(applyFix 実体)
1) 入口:CLI スクリプト php-cs-fixer
場所:リポジトリ直下の実行ファイル。Laravelでいう public/index.php に相当します。
やっていることは「Composerのオートローダを探して読み込み、Console Applicationを起動する」だけです。
#!/usr/bin/env php
<?php
$autoloadPaths = [
__DIR__.'/vendor/autoload.php',
__DIR__.'/../../autoload.php',
];
foreach ($autoloadPaths as $path) {
if (file_exists($path)) {
require_once $path;
break;
}
}
use PhpCsFixer\Console\Application;
$application = new Application();
$application->run();
ここが最初のエントリーポイントです。
vendor/autoload.php が見つからない場合、「Unable to locate autoload.php」が発生します。
2) Composer autoload:PSR-4 によるクラス解決
composer.json の設定により、PhpCsFixer\ 名前空間のクラスは src/ ディレクトリにマッピングされます。
"autoload": {
"psr-4": { "PhpCsFixer\\": "src/" }
},
"autoload-dev": {
"psr-4": { "PhpCsFixer\\Tests\\": "tests/" }
}
Composerは vendor/autoload.php を通してこのマッピングを登録し、クラスを自動的に読み込めるようにします。
開発者として本体を直接動かす場合は、このリポジトリ内で composer install か composer update を実行し、vendor/autoload.php を生成しておく必要があります。
3) Console Application:PhpCsFixer\Console\Application
Symfony Console をベースにしたアプリケーションの本体です。
ここで fix, list, help などのコマンドが登録されます。
class Application extends Symfony\Component\Console\Application
{
public function __construct()
{
parent::__construct('PHP CS Fixer', self::VERSION);
$this->add(new Command\FixCommand());
// 他にもコマンドが追加される
}
}
Application::run() が呼ばれると、ユーザの入力に応じて該当コマンドの execute() が実行されます。
4) FixCommand:PhpCsFixer\Console\Command\FixCommand
fix サブコマンドの本体です。設定の読込からRunnerの起動までをまとめています。
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = $this->resolveConfig($input);
$factory = new FixerFactory();
$factory->registerBuiltInFixers();
$fixers = $factory->useRuleSet($config->getRules())->getFixers();
$runner = new Runner(
$config->getFinder(),
$fixers,
$config->getDiffer(),
$config->getCacheManager(),
$input->getOption('dry-run')
);
$changed = $runner->fix();
return $changed ? self::FAILURE : self::SUCCESS;
}
ここまでで「どのファイルにどのルールをどう適用するか」が決まります。
FixerFactory は内部で組み込みFixerを登録し、設定ファイル(.php-cs-fixer.dist.php)に基づいて使うFixer群を抽出します。
5) Runner:PhpCsFixer\Runner\Runner
Runnerは、実際のファイル処理とFixer適用を担当する層です。
public function fix(): bool
{
$hasChanges = false;
foreach ($this->finder as $fileInfo) {
$code = file_get_contents($fileInfo->getPathname());
$tokens = Tokens::fromCode($code);
foreach ($this->fixers as $fixer) {
if ($fixer->isCandidate($tokens)) {
$fixer->applyFix($fileInfo, $tokens);
}
}
if ($tokens->isChanged()) {
$hasChanges = true;
$this->writeBack($fileInfo, $tokens);
}
}
return $hasChanges;
}
Finderで対象ファイルを列挙し、Tokens クラスでトークン化。
各Fixerの applyFix() を順に適用して、変更があればファイルを書き戻します。
6) Fixer:PhpCsFixer\Fixer\...
Fixerは、実際の整形処理を行う小さな単位のクラスです。
すべて FixerInterface を実装し、主要メソッドは applyFix() です。
final class PhpdocAlignFixer implements FixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
// PHPDocコメントが含まれるかなど、軽い適用条件チェック
}
public function applyFix(SplFileInfo $file, Tokens $tokens): void
{
// トークンを走査し、@paramなどを整列
}
}
入力は Tokens、出力も Tokens。
Fixerは「単一責務(1つのスタイル問題を直す)」で作られ、Runnerによって順に適用されます。
利用者と開発者のautoloadの違い
- 利用者は
./vendor/bin/php-cs-fixer(Composer生成のラッパ)を実行するだけでOK。
ラッパ側が自動で 自分のプロジェクトのvendor/autoload.phpを読み込みます。 - 開発者が本体リポを直接実行する場合は、このリポジトリの
vendor/autoload.phpを用意する必要があります。
つまりcomposer installをしないとCLIは起動しません。
autoloadの読み込み先が「プロジェクトか本体リポか」で異なる点を混同しないのが重要です。
全体まとめ
| レイヤ | ファイル | 役割 |
|---|---|---|
| CLI | php-cs-fixer |
実行スクリプト。autoloadを読み込み、Applicationを起動 |
| autoload | vendor/autoload.php |
PSR-4でクラスを自動解決 |
| Application | src/Console/Application.php |
fix/list等のCLIコマンド登録 |
| FixCommand | src/Console/Command/FixCommand.php |
設定読込・Runner起動 |
| Runner | src/Runner/Runner.php |
ファイル列挙・Fixer適用 |
| Fixer | src/Fixer/... |
整形ロジック(applyFix実装) |
まとめ
- エントリーポイントは
php-cs-fixerスクリプト。autoloadを経て Application → FixCommand → Runner → Fixer と流れる。 -
fixコマンドの実体はFixCommand::execute()内で、FixerFactoryとRunnerを中心に動く。 - Fixerはすべて
FixerInterfaceを実装し、トークン列を直接変換する。 - autoloadの文脈が「利用者」と「開発者」で異なる点に注意。
Discussion