PSRとコーディングスタイルの関係、そしてPERへ

今年に入ってからも短い間に結構いろんなところで説明しているので改めて書きます。

:::note
 • PSR-1は**高度な技術的相互運用性**を目的としたコーディング標準
 • PSR-2/→PSR-12は**プロジェクト内での認知的摩擦を軽減する**ためのスタイルガイド
 • PSR-12は**PER Coding Style**に進化した
:::

## PSRとは何か

PSRが**何ではないか**について下記の記事にまとめたので、あとでゆっくり読んでください。

https://qiita.com/tadsan/items/942a381e952e12a8fa5a

というか↑の記事にだいたい書いたので、この記事はコーディングスタイルに絞った内容ということになります。

重要な部分だけ抜萃して再掲しましょう。

> PSRを管理するのは[**PHP-FIG**]((https://www.php-fig.org/)) (*The PHP Framework Interoperability Group*、**PHPフレームワーク相互運用グループ**)です。この組織はPHPのフレームワーク・CMS・ツールなどの開発者の寄合所帯です。
>
> [Frequently Asked Questions - PHP-FIG](https://www.php-fig.org/faqs/)の内容をざっくり訳します。
>
> ### PHP-FIGの目的は何ですか？
>
> グループの背景にある考えは、代表者たちがプロジェクト間の共通点について話し合うことで協働する方法を探ることです。グループの議論の主な観客は参加プロジェクト同士ですが、そのほかのPHPコミュニティも注目していることはよく承知しています。ほかの人々がPSRの成果物を採用することは歓迎しますが、それは目的ではありません。あなたがどうやってアプリケーションを構築すればいいのかを教えてくれるひとは居ません。
>
> #### 投票メンバーは標準を遵守しなければいけませんか？
>
> いいえ。PHP-FIGの投票メンバーになることで、すべての、あるいは任意のPSRを実装することを強制されることはありません。プロジェクトはしかるべき時にアップグレードする際に後方互換性を考慮する必要があります。ほとんどのプロジェクトで最終的に採用されると想定されますが、必須ではありません。

:::note alert
PSRの直接の勧告対象はPHP-FIGの参加プロジェクトであり、***一般PHPerのわれわれではありません***。逆に勧告は会員プロジェクトの所属メンバーが完全遵守しなければならないという性質のものでもありません。
:::
:::note
とはいえComposerのオートロードに使われる名前空間規約の**PSR-4**、コードフォーマッターのルールセットとして採用されている**PSR-12**、フレームワークのHTTP標準として使われている**PSR-7**など、一般PHPerのわれわれも無視できない存在感があります。
PHPコミュニティのわれわれがPSRをベストプラクティスとして参照したり、PHP-FIGに所属しないプロジェクトがPSR準拠のライブラリを作成・公開することも排除されていません。
:::

[PHP-FIG](https://www.php-fig.org/)のページには各分野ごとにPSRが4分野にカテゴライズされています。

「オートローディング」「インターフェイス」「HTTP」そして「コーディングスタイル」です。

![スクリーンショット 2023-02-16 1.35.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/ff7f3bd8-275c-ab57-3494-42d38e9debb4.png)

ここに分類されているのが以下の3種類の勧告です。

 * **PER Coding Style**
 * **PSR-1: Basic Coding Standard**
 * **PSR-12: Extended Coding Style Guide**

以下ではこれらをそれぞれ見ていきましょう。

PHP-FIGの標準勧告(Standard Recommendation)は**勧告本文**と策定の背景などを記録した**メタドキュメント**の二部から構成されています。(ただし最初期のPSRにはメタドキュメントがないものもあり、PSR-2には補遺がAppendixとして記述されています)

## PSR-1: Basic Coding Standard

記事冒頭の抜萃にも書いた通り、PSR-1の目的は「**高度な技術的相互運用性**」です。

https://www.php-fig.org/psr/psr-1/

https://www.infiniteloop.co.jp/docs/psr/psr-1-basic-coding-standard.php

この勧告については本文の「1. Overview」としてまとめられていますが、相互運用性の観点で特に重要な部分をさらに抜萃しましょう。

 * ファイルには `<?php` と `<?=` タグだけを使うべし (MUST)
 * PHPコードのファイルはUTF-8(BOMなし)だけを使うべし (MUST)
 * ネームスペースとクラス名は「オートローディング」標準勧告(PSR-0, PSR-4)に従うべし (MUST)
 * ファイルにはシンボル宣言(クラス、関数、定数…)と副作用(出力の生成、`ini`設定)のどちらかを書くべし (SHOULD)
     * ファイル内には両者を書くべからず (SHOULD NOT)

PHPタグには`<?`という短いタグも利用できるのですが、これは`php.ini`の[`short_open_tag`](https://www.php.net/manual/ja/ini.core.php#ini.short-open-tag)やコンパイルオプションの`--disable-short-tags`で無効化できるため実行環境によって使えたり使えなかったりします。これがPSR-1に含まれているのは設定に依存する`<?`を使わないことでフレームワークやライブラリが多くの環境で動作するようにしようということです。

文字コードをUTF-8に統一しようというのも相互運用性のためです。**PHPに内部文字コードといったものはない**のですが、2023年現在のWebではUTF-8が標準的な文字コードになっている[^1]のでライブラリのエンコーディングをUTF-8に揃えることでプログラム内でエンコーディングを変換しなくても外部ライブラリの関数やクラスを利用できるようになります。

オートローディングの重要性については2012年に書かれたHirakuさんの文章を参照してもらうのが良いでしょう。

http://blog.tojiru.net/article/278307117.html

このオートローディングとファイルごとの副作用分離の恩恵を受けるのは、もちろんComposerなどを使ったクラスのオートロードです。名前空間/クラス名の先頭にベンダー名を位置付けることでクラスの帰属を明確にし、クラスがロードされた瞬間に奇妙な処理が実行されないように分離すべし(SHOULD)という紳士協定を定めることで実行結果がわかりやすくなることを意図していると考えられるでしょう。

プログラミングでは動作が定義されておらず何が起こるかわからないことを例えて<q>[鼻から悪魔が飛び出す](https://ja.wikipedia.org/wiki/%E6%9C%AA%E5%AE%9A%E7%BE%A9%E5%8B%95%E4%BD%9C)</q>と呼ばれていたり、「[事業をエンジニアリングする技術者たち](https://www.lambdanote.com/products/carta)」という本では副作用が想像できないことを例えて<q>部屋のドアノブを回すと風呂の底が抜ける</q>ようなシステムだと例えていました。

:::note alert
クラスをロードするだけで鼻から悪魔を出したり風呂の底が抜けるようなことはせず、クラスをロードする以外の余計なことをすべきではない、ということです
:::

PSR-1の概要からこの記事に抜萃しなかった部分では定数名・クラス名・メソッド名の命名規則について定められているのですが、正直これは相互運用性の観点からは相対的に重要ではないものです。これらの規則は後続のスタイルガイドに属するものでもよさそうに思いますが、最初期のPSRなので粒度が揃いきっていないのも仕方ないものと筆者は割り切っています。

## PSR-12: Extended Coding Style Guide

PSR-12はコーディングスタイルガイドです。PSR-1を前提として、さらに踏み込んだスタイルが定義されています。

PSR-12は前身となるPSR-2を置き換える形で定義された「拡張」と位置付けられており、PSR-2策定時に想定されていなかったケースの穴を埋めたり、それ以後に追加された構文のスタイルが追加されています。

https://www.php-fig.org/psr/psr-12/

PSR-12の前身(あるいは拡張元)となるPSR-2にはAppendixとして、PHP-FIGメンバーの各種のPHPコーディング標準の調査に基いた「一般的な慣行」、別の言い方をすると「妥協の結果」としてPSR-2を定めたことが伺えます。

https://www.php-fig.org/psr/psr-2/#appendix-a-survey

PSR-12のメタドキュメントにおいては、新しい慣例を強権的に確立しようとするのではなく、PSR-2からの再フォーマットで多くの差分が生じないように設計されたことが明記されています。

https://www.php-fig.org/psr/psr-12/meta/

重要なことはPSR-1と同様に概要に書いてあるので抜萃しましょう。

> Like PSR-2, the intent of this specification is to reduce cognitive friction when scanning code from different authors. It does so by enumerating a shared set of rules and expectations about how to format PHP code. This PSR seeks to provide a set way that coding style tools can implement, projects can declare adherence to and developers can easily relate to between different projects. When various authors collaborate across multiple projects, it helps to have one set of guidelines to be used among all those projects. Thus, the benefit of this guide is not in the rules themselves but the sharing of those rules.

> PSR-2とおなじく、この仕様の意図は別の作成者によって書かれたコードを読むときの認知的な摩擦を軽減することにあります。この仕様はコード整形についての一連の共有された規則と期待を列挙することで記述されます。このPSRはコーディングスタイルツールが実装できる方法を提供し、プロジェクトは標準への遵守を宣言することで開発者が違うプロジェクトを容易に結び付けられるようにすることを目的としています。さまざまな作成者が複数のプロジェクトで共同作業するとき、それらすべてのプロジェクトで使用されるひとつのガイドラインセットがあると役立ちます。したがって、このガイドの利点はルールそのものではなく、ルールを共有することにあります。

要はプロジェクトをまたいだ共同作業をする上でコーディングスタイルが同じだった方がコラボレーションしやすかろう、というのがこの標準勧告の目的です。

重要な要素として、PSR-1はコードの相互運用性、つまり**多くの環境で動かしやすくする**ことが第一の目的であるのに対して、PSR-12は純粋に審美的、つまり **「美しいコード」の基準を揃えてコードをを書く人間の認知的な摩擦を減らそう**ということが第一の目的です。

両者はコーディング標準という似たものを取り扱いながら、概念的に異なる目的を持った勧告であることをおわかりいただけますでしょうか。

## PSR-2

PSR-2はPSR-12の前身となるコーディングスタイルガイドです。基本的にはPSR-12は、PSR-2が策定された2012年(PHP 5.4)から2019年(PHP 7.3)の間に追加された構文への対応が追加されたものだと考えてください。

先も引いたところですが、[PSR-12のメタドキュメント](https://www.php-fig.org/psr/psr-12/meta/)には以下のようにあります。

> PSR-2 required adopters to reformat large amounts of existing code which stifled adoption. To help alleviate this issue with PSR-12, we have taken a more prescriptive approach and defined the standards for new language features as they are released.
>
> However it is for a lack of wanting to be dictatorial that we will aim to apply PSR-2 styling, rationale and stances (Described in Section 4, Approaches) in PSR-12 instead of establishing new conventions.

> PSR-2を採用するには既存コードを大量に再フォーマットする必要があり、採用の妨げになっていました。PSR-12はこの問題を軽減するために、より規範的なアプローチを採用し、(PSR-2から現在までに)リリースされた新しい言語機能についての標準スタイルを定義しました。
>
> しかし、新しい慣習を確立する代りにPSR-12ではPSR-2のスタイル、理論的根拠、スタンス(セクション4、アプローチで説明)を適用(踏襲)することを目指したのは、独裁者になりたいわけではないからです。

PSR-12の策定作業はPSR-2で未定義だった穴を埋めるものとして定義されており、PSR-2で定義されたものをまったく別のスタイルに書き換えたというものではありません。**PSR-12はPSR-2を拡張したもの**、**PSR-2はPSR-1を拡張したもの**です。

また、当時に既に7.4実装されることが決定していた`fn() => ...`はPSR-12のスコープ外です。

## PER Coding Style

**PER**(PHP Evolving Recommendations)は時間の経過によって変化する事柄についてアップデートされることを意図した「**進化する**」標準規格です。

:::note warn
以下の記述は2023年2月時点で現在進行中の事象を取り扱っています。
お読みの頃には情勢が変化している可能性があるので、ご注意ください。
:::

現在有効な**PER**は**PER Coding Style**だけです。「**PER**」はコーディングスタイルだけのものではないので「**PER Coding Style**」とセットで呼ぶ必要があります。

2022年1月に策定された**PER Coding Style**(1.0.0)はPSR-12の文章の言い回しを一部調整した程度に留まっており、ほぼ「**PSR-12 ≒ PER CodingStyle 1.0.0**」ということができます。ただし、PSR-2とPSR-12の関係とは異なり、*PSR-12はまだ廃止されていません*。

https://www.php-fig.org/per/coding-style/

問題は、このあと **PER Coding Style (2.0.0)** として勧告されるであろう差分です。

https://github.com/php-fig/per-coding-style/compare/1.0.0...f2cdd2c8114fa55ca95d3fc357be2a283b2f6e0c

PHP 7.4から2023年現在最新のPHP 8.2までの間に追加された言語機能についての定義が含まれています。

 * `fn() => ...` (Short Closures)
 * `match`
 * `#[Attributes]`
 * `readonly`

インパクトが大きい変更としては、Short Closuresが `fn () => ...` ではなく `fn() => ...` (`fn`の直後に**スペースを入れない**)として記載されていることでしょうか。私はスペース入れない派だったので大きな問題はありませんが、入れてるコードもそれなりに見るので大変です。

また、**Trailing commas**(末尾のコンマ、いわゆるケツカンマ)と**Heredoc and Nowdoc**も独立した章で言及されています。

> If the list is split across multiple lines, then the last item MUST have a trailing comma.

> リストが複数の行に分割されているなら、最後の項目には末尾のコンマが必要です(MUST)。

:::note
ここまで言及していませんでしたが、PSR-12は**SHOULD**(すべき)はできるだけ使わず、**MUST**(しなければならない)または**MUST NOT**(してはならない)で記述されています。
:::

この「リスト」というのは配列だけではなく、パラメータリストや`match`も含んでいます。

```php
function beep(
    string $a,
    string $b,
    string $c,
) {
    // ...
}
```

問題は、パラメータ宣言の末尾に余分な`,`を書けるようになったのはPHP 8.0からということです。[PHP 7.4は2022年12月末をもって**EOL**(end of life)になりました](https://www.php.net/supported-versions.php)からね… 仕方ないですね……。

## スタイルガイドとコードフォーマッターの意義

「**俺たちはPHP-FIGのメンバーじゃないからPSRだろうがPERだろうが関係ねえ**」と切って捨てられれば簡単なのですが、幸か不幸かPSRという言葉は現代PHPのコーディング標準として存在感を持っています。

2023年においてPSR-12の対抗馬となる完成度のスタイルガイドがあるかといえば、ないのが実際のところです。Symfonyはかつて独自のコーディング標準を持っていましたが、[現在のSymfonyコーディング標準](https://symfony.com/doc/current/contributing/code/standards.html)はPSR-12をベースに拡張しています。

さて、世の中には**コードフォーマッター**というソースコードをコーディング標準に適合するように修正してくれるツールがあります。PhpStormやVS Code(Intelephense)もフォーマット機能を提供していますが、高度なカスタマイズの共有や相互運用性を考えると[PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer)や[PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer)(phpcbf)、[EasyCodingStandard](https://github.com/easy-coding-standard/easy-coding-standard)(ecs)のようなツールを使うのがよいでしょう。

これらのツールはPSR-12ルールセットを提供しているので、それを基準とした差分としてハウスルールを定義できます。理想的には各開発メンバーはGitコミットする前に各自でフォーマットしてもらうことを徹底できれば、フォーマットのために余分なコミットを積むことを最小限にできるでしょう。

:::note
[EasyCodingStandard](https://github.com/easy-coding-standard/easy-coding-standard)(ECS)というツールを始めて聞いた方もいらっしゃるかもしれませんが、これはリファクタリングツールとして知られる[Rector](https://github.com/rectorphp/rector)などを開発している[Tomas Votruba](https://github.com/TomasVotruba)氏が開発しているコードフォーマットツールです。ECSはPHP-CS-FixerとPHP_CodeSnifferのルールを統合できる、パフォーマンスに優れる、`composer require --dev`でインストールしても[外部パッケージへの依存を持たない](https://qiita.com/tadsan/items/c77615e39f934866c602)など優れた特徴がいくつもあります。
:::

## まとめ

もうなんでもいいからルールとツール決めてフォーマットしろ。コーディング標準なんか典型的な[自転車置場の議論](http://0xcc.net/blog/archives/000135.html)なんだから、いい大人が会社で喧嘩すんな。

## 本当にどうでもいいこと

「**PER Coding Style**」って日本語で声に出して言うとき、どうやると伝わりやすいんでしょうね。<ruby>ＰＳＲ<rp>(</rp><rt>ピーエスアール</rt><rp>)</rp></ruby>みたいに<ruby>ＰＥＲ<rp>(</rp><rt>ピーイーアール</rt><rp>)</rp></ruby>でもいいんですが… もう「<ruby>**PER Coding Style**<rp>(</rp><rt>パー・コーディングスタイル</rt><rp>)</rp></ruby>」とかでいいのでは。

[^1]: 実際にHTML5では[`charset="utf-8"`だけが有効なエンコーディング](https://developer.mozilla.org/ja/docs/Web/HTML/Element/meta#attr-charset)とされています