(jp) =
<!–
–>

昨日、PSR の抽象化に関する Matthias Noback の優れたブログ投稿を読みました。このトピックについての考えも共有したいと思います。 回答したい部分を引用しますが、Matthias の視点について詳しく知りたい場合は、投稿全体を必ずお読みください。
個人を軽視するつもりはないことを前もって明確にしたいと思います。この投稿でそれを伝えるために一生懸命努力しました. それでも何かが無礼または人を傷つけると思われる場合は、私に連絡してお知らせください。喜んで再訪いたします。
最初から始めましょう。
数年前、PHP-FIG が最初の PSR を作成したとき、彼らは PHP エコシステムに大きな変化をもたらしました。
彼らは間違いなくそうしました。 FIG は、PHP エコシステムの近代化において素晴らしい仕事をしたと思います。
コーディング標準の PSR が定義されました。これにより、多くのチームがコーディング標準の議論を後回しにすることができたと確信しています。
ほとんどのプロの開発者は、知識の有無にかかわらず、少なくともある程度の PSR を使用していると言っても過言ではありません。
次は、フレームワークの相互運用性という大きな目標を目指した PSR でした。 […] アイデア […] フレームワークは、提案されたインターフェースの実装パッケージを提供できるということでした。 したがって、最終的には Symfony ルーター、Zend コンテナー、Laravel セキュリティ コンポーネントなどを使用できます。
フレームワークの相互運用性はある程度は役に立ちますが、すべてを連携させようとするのは非現実的です。 ルーターまたはコンテナーを実装する方法は非常に多くあります。異なるフレームワーク間で共有される共通のインターフェイス セットが 1 つある場合、それらのフレームワークのユーザーにほとんど違いはありません。 確かに実装の違いはあるかもしれませんが、フレームワークの目標は、ユーザーがそれらを気にせず、代わりにアプリケーションの構築に集中できるようにすることです。
マティアスはこの懸念を共有しています:
PSR の抽象化に関して私が個人的に抱いていた懸念の 1 つは、優れた抽象化ができれば、複数の実装パッケージは必要ないということです。
私たちのコミュニティの 2 つまたは 3 つ (数え方によっては 4 つまたは 5 つ) の主要なフレームワークの利点は、それぞれが問題に取り組む独自の方法を持っていることです。 各フレームワークには、さまざまな開発者グループを引き付ける独自のアイデンティティがあります。
「完全なフレームワークの相互運用性」を目指すことは、非現実的な目標であるだけでなく、必要のないものでもあります。
しかし、まあまあ、マティアスとFIGは話していないのかもしれません 満杯 フレームワークの相互運用性、ほんの一部です。 それでは、抽象化について話しましょう。 Matthias 氏は、PSR には価値があると述べています。PSR は試行錯誤された抽象化であり、信頼できるからです。
たとえば、PSR-18 の HTTP クライアント インターフェイスを見てみましょう。
interface ClientInterface
public function sendRequest(
RequestInterface $request
): ResponseInterface;
確かに、これは HTTP クライアントが得ることができるのと同じくらい簡単です。 リクエストを送信し、レスポンスを返すことができる必要があります。 それについて、マティアスは次のように語っています。
それはすでに素晴らしい抽象化です: それはあなたが必要とすることを行い、それ以上でもそれ以下でもありません。 結局のところ、必要なのは HTTP リクエストを送信し、返された HTTP レスポンスで何かを行うことです。
しかし、もちろん、その悪名高い RequestInterface. 再びマティアスです:
このインターフェースに関する唯一の問題は、おそらく、RequestInterface インスタンスをどのように作成できるかということです。
その想いに共感できます。 PSR-7 準拠のライブラリに遭遇するたびに、立ち止まって考え、どのパッケージが簡単に作成できるかを検索する必要があります。
HTTP クライアントが必要になるたびに、これにまた苦労します。インストールするパッケージと、これらのオブジェクトを取得する方法は?
Matthias は、おそらく独自のより単純な抽象化がより良い選択肢であるという考えを熟考していますか?
interface HttpClient
public function get(
string $uri,
array $headers,
array $query
): string;
public function post(
string $uri,
array $headers,
string $body
): string;
「残念ながら」と彼は言います:
[…] 独自の抽象化を作成すると、次のような確立された抽象化を使用する利点が失われます。
- 自分で適切な抽象化を設計する必要はありません。
- インターフェイスを使用し、実装パッケージに依存して適切な実装を提供できます。 別のパッケージの方が優れていることがわかった場合は、非常に簡単に切り替えることができます。
最近、FIG に関する私の問題の核心に到達しようとしています。 確かに、すべてのプロジェクトで車輪を再発明するのではなく、十分に試行されたコードを使用することには価値があります。 Matthias は、その危険性について次のように警告しています。
PSR インターフェイスを独自のクラスでラップすると、これらの利点が失われます。 正しくない抽象化や、簡単に置き換えることができない重い実装を必要とする抽象化を作成することになるかもしれません。
独自の実装を実行する場合と比較して PSR を使用する利点は、独自の実装によって、以前に FIG によって回答された多くの疑問が生じることです。
- $headers 配列の構造は何ですか: キーとしてのヘッダー名、値としてのヘッダー値? すべての弦?
- $query; についても同じです。 しかし、配列のようなクエリ パラメータをサポートしていますか? クエリは URI の一部であるべきではありませんか?
- $uri にはサーバーのホスト名も含めるべきですか?
- get または post 以外のリクエスト メソッドを使用したい場合はどうすればよいでしょうか。
- 本文なしで POST リクエストを作成したい場合はどうすればよいでしょうか?
- POST リクエストにクエリ パラメータを追加したい場合はどうすればよいでしょうか。
- どうすれば失敗に対処できるでしょうか。 常に文字列を取得しますか? これらのメソッドはどのような例外をスローしますか?
では、すでに抽象化されているのに、別の抽象化を作成するために時間とお金を費やす必要はありません。
さて、あなたは… FIG が常に最良の抽象化を思いつくとは限らないという事実を考慮しましたか? 少数の開発者グループが何ヶ月もかけて議論し、最終的にいくつかのメソッドを含むインターフェースを考え出すプロセスは、実際には開発者が実際に扱っている問題を解決しないのでしょうか?
HTTP 要求と応答を作成するという質問は、FIG によって確実に回答されています。 これは を 答え。 Symfony と Laravel の両方に独自の答えがあり、私に尋ねればより簡単です。 さらに、私のユースケースに対するより良い答えかもしれません。
私たちは HTTP の抽象化について話してきましたが、Matthias は別の例を挙げています: コンテナ インターフェイスです。 彼は、FIG がコミュニティのニーズに関連する抽象化を常に考え出すとは限らないことに同意します。
それに費やされた努力や関係者の信用を傷つける意味はありませんが、私の意見 PSR-11: Container interface のように、時代遅れになってしまう標準が常に存在します。
Matthias はここで「時代遅れ」という言葉を慎重に使用していますが、私はそれがまったく無関係な抽象化であると言いたいと思います。 同様に、PSR-7 は、私が Laravel や Symfony のプロジェクトで行っているほとんどの作業 (および他の多くの作業) とは無関係です。 「相互運用性」を捨てて、フレームワークに密接に結びついたプロジェクトを構築していて、それを変更するつもりがない場合でも、それはどういう意味ですか? これは抽象化でもあり、優れたものでもありますが、FIG に裏打ちされた「公式」の名前を持つものではありません。
今、何人かが私に「未来を予測することはできない。 行う 他の人についてはわかりませんが、実際にはクライアントとの契約でプロジェクトの範囲を概説しています。クライアントは、1 つのフレームワークで具体的にアプリケーションを作成するために私たちに支払います。このレベルの相互運用性は必要ありません。 .
たとえば、Symfony と Laravel の両方のコンテナー インターフェイスの実装を見てください。 \Psr\Container\ContainerInterface、それでも両方とも非常に多くのメソッドを追加します。 PSR-11 の実装は、フレームワークが「はい、私たちは PSR に準拠しています」と言うことができるようにするための仕掛けにすぎません。 あるから これら2つの間の実際の相互運用性はありません.
別の例: PSR-7、HTTP メッセージ PSR。 Symfony と Laravel の両方が PSR-7 を実装していません。 ああ、「はい、私たちは PSR に準拠しています」と言えるように、基本的にアダプター パターンを適用する PSR-7 ブリッジがあります。
アダプターパターンを適用することは 正反対 FIGが達成しようとしていることは何ですか? FIG はフレームワーク全体で共通の抽象化を使用することを望んでいますが、アダプター パターンでは 1 つのインターフェイスを次のように使用できます。 別 インターフェース。
Matthias の最後の文を少し変更して終わりにしたいと思います。
同時に、PSR 抽象化は、多くの設計作業を節約し、コードがベンダー パッケージの変更の影響を受けにくくなるため、意味がある場合はいつでも使用する必要があります。
私は次のように言います: 抽象化 コンテナ、HTTP クライアント、キャッシング、キューイングなどの実証済みの Laravel または Symfony の実装であるかどうかに関係なく、これらの実装は 仕事. 「PSR」という名前が付いていなくても、それらは有効な抽象化です。
はい、抽象化を使用する必要があります。 いいえ、無関係で時代遅れの抽象化を使用するべきではありません。 抽象化が「PSR」という名前を持っているからといって、それが他のものよりも突然優れているわけではありません。
この投稿で怒る人もいると思います。 あなたは許可されています、私はそのフィードバックを受け入れます。 メールでご連絡いただき、ご意見をお聞かせください。 FIGの誠意と努力を疑うつもりはありません。 私は彼らが存在しない問題を解決しようとしていると心から信じています.
FIG は PHP コミュニティに大きな影響を与えました。彼らがパイオニアとして行った初期の作業に非常に感謝しており、コミュニティ全体がそれを認める必要があります。 また、FIG は目標を達成したと考えており、プロジェクトは完了したと言えます。
補足として、もう一点申し上げたいと思います。 この投稿を公開する前に、/r/php で Matthias の元の投稿を共有しました。 それについていくつかの洞察に満ちた議論があり、PSR-11 の作者である Matthieu Napoli が参加しました。
彼が言ったことの 1 つに対処したいと思います。なぜなら、この投稿を読んだ後に人々が持ち出す反論かもしれないと思うからです。 彼は言った:
PSR-11 は、コンテナーと相互運用したいライブラリに最適です。たとえば、次のようになります。
- フレームワークのコンテナーからシード クラスをロードできるようにする Phinx
- 機能コンテキストで依存性注入を行うための Behat
- Tactician コマンド バス: コンテナーからロード (https://tactician.thephpleague.com/plugins/container/)
- 拡張機能を扱う Faker
- コンテナーからハンドラーを遅延読み込みするための schmittjoh/serializer
言い換えれば、私は主に Symfony や Laravel のようなフレームワークの観点から PSR を見ており、Matthieu はより小さなスタンドアロンのパッケージについて考えています。
Matthieu が実際に示した例の 1 つを次に示します。 Phinx はオプションで、シード クラスを解決するために使用するコンテナー インスタンスの設定をサポートします。
if ($this->container !== null)
$seed = $this->container->get($class);
そして、公平に言えば、少なくとも小規模なスタンドアロン パッケージでは、ある程度の採用が見られるようです。 しかし、より広い文脈についてはどうでしょうか。 それらの機能は実際にエンド ユーザーによって使用されていますか? それらのパッケージがサードパーティの抽象化に頼るのではなく、アダプター層を提供した場合、さらに悪いことになるでしょうか? がたくさんあります もしも ここで、この種のパッケージを実際に使用した経験のあるユーザーの意見を聞きたいです。
私は主にフレームワークの観点から FIG を見ています。それは PHP であるため、関連があると思います。 フレームワーク 相互運用性グループ。 私は適切な抽象化のメリットを完全に否定しているわけではありません。
//platform.twitter.com/widgets.js