PHP コンポーネントとフレームワークの数は気が遠くなるでしょう。 Symfony や Laravel のような巨大なフレームワークもあれば、Silex や Slim のようなマイクロ フレームワークもあります。 CodeIgniter など、最新の PHP コンポーネントが登場するずっと前から存在していたレガシー フレームワークもあります。最新の PHP エコシステムはまさにコードのるつぼであり、開発者が素晴らしいアプリケーションを構築するのに役立ちます。
残念ながら、これらの古い PHP フレームワークは比較的閉鎖的な環境で開発されており、他の PHP フレームワークとコードを共有できませんでした。プロジェクトで古い PHP フレームワークを使用している場合、フレームワークにしっかりと閉じ込められ、フレームワーク自体のエコシステムに囚われて抜け出すことができなくなります。フレームワーク自体が提供するツールに満足している場合は、この比較的集中化された環境で問題はありません。ただし、CodeIgniter フレームワークを使用しており、Symfony フレームワークからいくつかの優れたクラス ライブラリを選択して使用したい場合は、プロジェクト専用のワンタイム アダプターを作成しない限り、それほど幸運ではないかもしれません。
ここで私たちが得ているのは、コミュニケーションの失敗です(ここではまったくコミュニケーションができません)
-映画「Blood Behind Bars」の古典的なセリフ
問題がわかりますか?クローズド環境で開発されたフレームワークは、他のフレームワークと相互運用するように設計されていません。この設計アプローチは、開発者 (選択したフレームワークによって創造性が制限される) にとっても、フレームワーク自体 (既存のコードを再開発する人) にとっても非常に非効率です。それでも、皆さんに良いお知らせがあります。 PHP コミュニティのフレームワークに対する理解は、集中型フレームワーク モデルから、効率的でパブリックな専用コンポーネントで構成される分散型エコシステムへと移行しました。
PHP-FIG の救済計画
一部の PHP フレームワーク開発者はこの問題に気づき、2009 年の php|tek (有名な PHP カンファレンス) で議論しました。議論の中心は、フレームワーク内で相互運用性と開発効率をどのように向上させるかということです。たとえば、PHP フレームワークが monolog のような非結合ロギング クラスを共有できる場合、毎回新しい密結合ロギング クラスを開発する必要はないのでしょうか。 PHP フレームワークが Symfony フレームワークの symfon/httpfoundation コンポーネントの優れた HTTP リクエストおよびレスポンス クラスを使用できる場合、独自の HTTP リクエストおよびレスポンス クラスを開発する必要はないのでしょうか?この目標を達成するには、PHP フレームワークは何らかの共通言語を使用してフレームワークと対話し、共有する必要があります。そして彼らに必要なのは標準です。
php|tek カンファレンスで偶然出会ったこれらの PHP フレームワーク開発者は、最終的に PHP Framwork Interop Group (PHP-FIG) を設立しました。 PHP-FIG は、PHP フレームワーク プロジェクトの代表者のグループで構成されており、PHP-FIG の公式 Web サイトによると、彼らの責任は「それぞれのプロジェクトの共通点について話し合い、連携する方法を見つけること」です。 PHP-FIG は、いくつかの推奨事項 (推奨事項) を開発しました。PHP フレームワークは、対話性を向上させ、それぞれのフレームワークを共有するために、これらの推奨事項を実装するかどうかを自主的に選択できます。
PHP-FIG は、PHP フレームワーク プロジェクトの代表者のグループによって設立された組織です。そのメンバーは選挙で選ばれたものではなく、PHP コミュニティを発展させたいという彼らの願望以外に特別な点はありません。どなたでも会員登録が可能です。提案段階にある PHP-FIG の推奨事項について、誰でもフィードバックを送信できます。最終的な PHP-FIG 推奨事項は、多くの場合、最大かつ最も人気のある PHP フレームワークの多くで採用され、実装されます。フィードバックを送信し、お気に入りの PHP フレームワークの将来の形成に協力して、PHP-FIG に参加することを強くお勧めします。
PHP-FIG はさまざまな推奨事項を提供する責任があるとよく言われますが、推奨事項はルールと同等ではなく、推奨事項を実装する必要はないため、これを理解することが非常に重要です。これらの推奨事項は慎重に検討され、慎重に作成された提案であり、PHP 開発者 (さらには PHP フレームワーク作成者) にさらなる利便性をもたらすことができます。
フレームワークのコラボレーション
PHP-FIG の使命は、フレームワークのコラボレーションを実現することです。フレームワークのコラボレーションとは、インターフェイス、自動読み込み、コーディング スタイルを通じて連携することを意味します。
インターフェース
PHP フレームワークは、共有インターフェースを通じて連携します。 PHP インターフェイスを介して、フレームワークは、サードパーティの依存ライブラリがこれらのインターフェイスをどのように実装するかを考慮せずに、サードパーティの依存ライブラリによってどのメソッドが提供されているかを確認できます。
PHP インターフェイスの詳細な説明については、第 2 章を参照してください。
たとえば、特定の PHP フレームワークがサードパーティのロガー オブジェクトを喜んで共有し、このオブジェクトがEmergency()、alert()、critical()、error()、warning()、notice()、を実装していると仮定します。 info() メソッドと debug() メソッド。これらのメソッドがどのように実装されるかは正確には問題ではありません。各フレームワークは、サードパーティの依存ライブラリがこれらのメソッドを実装しているかどうかのみを考慮する必要があります。
インターフェイスを使用すると、PHP 開発者は、かさばるフレームワークの代わりに、機能固有のコンポーネントを構築、共有、使用できます。
自動読み込み
PHP フレームワークは自動読み込みを通じて連携して動作します。オートロード機能により、PHP インタープリターは実行時に必要に応じて PHP クラスを自動的に見つけてロードできます。
PHP 標準が確立される前は、PHP コンポーネントとフレームワークは、マジック関数 _autoload() またはより高度な spl_autoload_register() メソッドを使用して、独自の独立したオートローダーを実装していました。そのため、使用するには各コンポーネントとフレームワークの固有のオートローダーに精通する必要があります。現在、最新の PHP コンポーネントとフレームワークのほとんどは、一般的なオートローダー標準と互換性があります。これは、統合されたオートローダーを使用して、複数の PHP コンポーネントを組み合わせて一致させることができることを意味します。
コーディング スタイル
PHP フレームワークはコーディング スタイルを通じて連携します。コーディング スタイルによって、スペース、大文字の使用、および (特に重要) 括弧の配置が決まります。すべての PHP フレームワークが標準のコーディング スタイルを統一できれば、PHP 開発者は新しい PHP フレームワークを使用するたびに新しいコーディング スタイルに適応する必要がなくなり、PHP フレームワークのコードがそれほど奇妙に見えることもなくなります。標準的なコーディング スタイルのセットにより、プロジェクトへの初心者の参入障壁も低くなり、不慣れなコーディング スタイルの学習に多くの時間を費やすことなく、バグの解決に集中できます。
標準的なコーディング スタイルは、私たち自身のプロジェクトを改善することもできます。すべての開発者には独自のコーディングの癖があり、開発者が同じコード ベースで共同作業する場合、問題が発生する可能性があります。標準的なコーディング スタイルは、コードの作成者に関係なく、すべてのチーム メンバーが同じコード ベース内の互いのコードをすぐに理解するのに役立ちます。
PSRとは何ですか?
PSR は PHP Standard Recommendation の略です。最近 PHP ブログを読んだことがある方は、PSR-1、PSR-2、PSR-3 などの用語を見たことがあるかもしれません。これらは PHP-FIG の推奨事項です。これらの名前は PSR- で始まり、数字で終わります。各 PHP-FIG 推奨事項は、ほとんどの PHP フレームワークで一般的に発生する特定の問題を解決するように設計されています。さまざまな PHP フレームワークが常に同じ問題を解決することを防ぐために、これらの PHP-FIG 推奨事項を採用し、共有ソリューションに基づいて独自のフレームワークを構築できます。
この本のリリース時点で、PHP-FIG は 5 つの推奨事項をリリースしました:
数えてみると、推奨事項は 4 つしかないことがわかります。はい、PHP-FIG は初期の PSR-0 推奨事項を放棄しました。元の推奨事項は最新の PSR-4 に置き換えられました。
PHP-FIG からのこれらの推奨事項は、前に述べた 3 つの連携方法 (インターフェイス、自動読み込み、コーディング スタイル) に完全に一致していることに注意してください。これは偶然ではありません。
PHP-FIG からのこれらの推奨事項に本当に興奮しています。これらは現代の PHP エコシステムの基礎です。これらは、PHP コンポーネントとフレームワークが連携して動作する方法を定義します。 PHP 標準について議論することは、人目を引くトピックではないことは認めますが、(私の考えでは) PHP 標準は最新の PHP を理解するための前提条件です。
PSR-1: 基本的なコーディング スタイル
作成するコードに PHP コミュニティ標準との互換性を持たせたい場合は、PSR-1 から始めます。これは最も簡単に使用できる標準です。非常にシンプルなので、すでにこれらの標準を使用していることに気づかないかもしれません。 PSR-1 は、それほど労力をかけずに実装できるシンプルなガイドラインを提供します。 PSR-1 の設計目的は、PHP フレームワークに参加できるベースラインのコーディング スタイルを提供することです。 PSR-1 標準と互換性を保つには、次の要件を満たす必要があります。
PHP タグ
PHP コードを または = ?> に含める必要があります。間。他の PHP タグ構文は使用できません。
文字エンコーディング
すべての PHP ファイルは、バイト オーダー マーク (BOM) を含まない UTF-8 エンコーディングを使用する必要があります。これは少し面倒に思えますが、ほとんどのエディターまたは IDE は自動的にこれを処理できます
ファイルの使用法
別の PHP ファイルで、宣言 (クラス、特性、関数、定数など) を定義するか、結果を生成する操作 (情報の出力やデータの変更など) を実行します。しかし、両方を行うことはできません。これは簡単な作業であり、少しの先見性と計画が必要なだけです。
オートロード
PHP 名前空間とクラスは、PSR-4 オートロード標準をサポートする必要があります。あなたがしなければならないことは、PHP 宣言に適切な名前を付け、その定義ファイルが正しいパスに保存されていることを確認することだけです。 PSR-4については後ほど説明します。
クラス名
PHP クラス名には、一般的な CamelCase 形式を使用する必要があります。この形式は TitleCase とも呼ばれます。たとえば、CoffeeGrinder、CoffeeBean、PourOver などです。
定数名
PHP 定数はすべて大文字である必要があります。必要に応じて、アンダースコアを使用して単語を区切ります。たとえば、WOOT、LET_OUR_POWERS_COMBINE、GREAT_SCOTT などです。
メソッド名
PHP メソッド名には、一般的なキャメルケース (小文字のキャメルケース) 形式を使用する必要があります。これは、メソッド名の最初の文字は小文字であり、後続の各サブワードの最初の文字は大文字のままであることを意味します。たとえば、phpIsAwesome、iLoveBacon、tennantIsMyFavoriteDoctorなどです。
PSR-2: 厳密なコーディング スタイル
PSR-1 標準を実装した後の次のステップは、PSR-2 標準を実装することです。 PSR-2 標準は、より厳格なガイドラインに従って PHP コーディング スタイルを定義します。
PSR-2 コーディング スタイルは、独自のコーディング スタイルと好みを共有する世界中の多くの貢献者から PHP フレームワークへの贈り物です。共通の厳密なコーディング スタイルにより、開発者は他の人にとって理解しやすいコードを作成できます。
PSR-1 とは異なり、PSR-2 の推奨事項にはより厳格なガイドラインが含まれています。 PSR-2 ガイドラインの一部は、期待どおりではない場合があります。ただし、PSR-2 は、最も一般的な PHP フレームワークで依然として推奨されるコーディング スタイルです。 PSR-2 を使用する必要はありませんが、使用すると、他の開発者にとって PHP コードの読みやすさと使いやすさが大幅に向上します。
厳密という言葉を使用していますが、実際には、もっと書くと慣れるでしょう。さらに、コードを PSR-2 スタイルに自動的にフォーマットできるツールがいくつかあります。
PSR-1
PSR-2 コーディング スタイルを実装するための前提条件は、まず PSR-1 コーディング スタイルを実装する必要があることです
インデント
これは、2 つの陣営の間でホットな話題です。一方の当事者は、コードのインデントにタブ文字を使用することを好みます。反対側 (よりクールな側) は、コードのインデントを実現するためにいくつかのスペースを使用することを好みます。 PSR-2 勧告では、PHP コードは 4 つのスペースを使用してインデントする必要があると規定しています
個人的な経験によれば、スペースの幅はコード エディタが異なってもほぼ同じであるため、スペース文字の方がインデントに適しています。タブ文字の幅はコード エディタによって異なります。 4 つのスペース文字を使用してコードをインデントすると、コードの視覚的な一貫性が最大限に保たれます。
PHP ファイルでは、各行の終わりに Unix 改行 (LF) を使用する必要があります。ファイルの最後には空白行が必要です。PHP タグは使用できません。終わりとして。コードの各行は 80 文字以下にする必要があります。たとえ基準が緩和されたとしても、最終的にはコードの各行は 120 文字を超えることはできません。各行の末尾に余分なスペースを含めることはできません。これは大変な作業のように思えますが、重要なことです。ほとんどのコード エディターは、コードを自動的に幅に合わせて折り返したり、末尾の null 文字を除外したり、Unix 改行を使用したりできます。これらの問題はすべて、毎回考える必要がなく、自動的に処理される必要があります。
多くの PHP 開発者が TRUE、FALSE、NULL をすべて大文字で書いていることを私は知っています。このような場合は、この習慣をやめて、今後はすべて小文字に切り替えてください。 PSR-2 勧告では、すべての PHP キーワードをすべて小文字で記述する必要があると規定されています。
名前空間
各名前空間宣言の後には空行が必要です。同様に、 use キーワードを使用して名前空間またはそのエイリアスをインポートする場合は、 use ステートメントの後に空行を追加する必要があります。次の例を参照してください。
namespace My\Component;use Symfony\Components\HttpFoundation\Request;use Symfony\Components\HttpFoundation\Response;class App{ // 类的定义体}
とインデント 同様に、クラス定義内の括弧も、よく議論されているトピックです。クラス名の後の同じ行に左括弧を置くことを好む人もいれば、クラス名の後の新しい行に左括弧を置くことを好む人もいます。 PSR-2 勧告では、次の例のように、クラス定義の開き括弧はクラス名の定義ステートメントの後の新しい行に配置する必要があると規定されています。クラス定義の閉じ括弧は、クラス定義本体の終了後の新しい行に配置する必要があります。おそらくあなたは私たちが言ったことをすでに実行しているので、それは大したことではありません。クラスが他のクラスから継承するか、他のインターフェイスを実装する場合、extends キーワードとimplements キーワードはクラス名と同じ行に置く必要があります。クラス定義の大括弧の位置。メソッド定義の開き括弧は、メソッド名の後の新しい行に配置されます。メソッド定義の閉じ括弧は、メソッド定義本体の末尾の新しい行に配置されます。メソッドのパラメータに注意してください。最初のかっこの後にスペースがあってはならず、最後のかっこの前にもスペースがあってはなりません。各メソッド パラメータ (最後のパラメータを除く) の後にはコンマとスペースが続きます:
<?php namespace My\App;class Administrator extends User{ // 类的定义体}
<?phpnamespace Animals;class StrawNeckedIbis{ public function flapWings($numberOfTimes = 3, $speed = 'fast') { // 方法定义体 }}
これは、おそらく私が最も間違えやすいガイドラインです。すべての制御構造キーワードの後には、単一のヌル文字が続く必要があります。制御構造のキーワードには、if、elseif、else、switch、case、while、do while、for、foreach、try、catch が含まれます。制御構造のキーワードに一対のかっこが必要な場合は、最初のかっこの後ろと最後のかっこの前にスペースがないことを確認してください。クラス定義やメソッド定義とは異なり、左括弧は制御構造キーワードの後に配置し、制御構造キーワードと同じ行に置く必要があります。制御構造キーワードの閉じ括弧は新しい行で開始する必要があります。これらのガイドラインを説明する簡単な例を次に示します。
<?phpnamespace Animals;class StrawNeckedIbis{ // 设置了作用域的静态属性 public static $numberOfBirds = 0; // 设置了作用域的方法 public function __construct() { static::$numberOfBirds++; }}
Fabien Potencier の PHP-CS-Fixer を使用して、ほとんどのコードの非互換性を自動的に修正することもできます。このツールはまだ完璧ではありませんが、これを使用すると、ほとんど労力をかけずに PSR との互換性を最大限に確保できます。
PSR-3: ロガーインターフェイス
一个logger是一个可以依照各种不同的重要程度来将信息写到给定输出的对象。记录下的信息可以用来分析、检查和修正应用程序的操作、稳定性和性能。例如在开发过程中将调试信息写入到文本文件中,将网站访问统计保存到数据库中,或者将致命错误的分析报告通过邮件发送网站管理员。目前最流行的PHP logger组件是 monolog/monolog,作者是Jordi Boggiano。
许多PHP框架在日志记录上有着不同的需求。在PHP-FIG成立之前,每个框架解决日志记录的方式都各不相同,通常都会有一套自己特有的实现。本着协同工作和专业化(现代PHP不变的主题)的精神,PHP-FIG建立了PSR-3 logger接口。框架接受兼容PSR-3的logger来实现两个重要的目标:将日志记录的开发工作交给第三方来完成,框架的使用者可以选择他们喜欢的logger组件。对大家来说这是一个双赢的结果。
写一个PSR-3的Logger类
一个兼容PSR-3推荐标准的PHP logger组件必须包含一个实现了Psr\Log\LoggerInterface接口的类。PSR-3接口复制了 RFC 5424 系统日志协议并且定义了九个方法:
<?phpnamespace Psr\Log;interface LoggerInterface{ public function emergency($message, array $context = array()); public function alert($message, array $context = array()); public function critical($message, array $context = array()); public function error($message, array $context = array()); public function warning($message, array $context = array()); public function notice($message, array $context = array()); public function info($message, array $context = array()); public function debug($message, array $context = array()); public function log($level, $message, array $context = array())}
使用$context参数可以构造出复杂的logger信息。你可以在信息中使用placeholders(占位符)。一个占位符看起来就像{placeholder_name}这样,它有开始花括号、占位符名称、结束花括号组成。占位符中不可以包含空格。而$context参数是一个关联数组,它的键就对应着占位符的名称(两个括号中间部分),而它的值可以用来替换消息文本中对应的占位符。
要写一个PSR-3的logger,创建一个新的PHP类,实现Psr\Log\Logger接口并为每个接口方法提供具体的实现就可以了。
使用PSR-3 Logger
如果你在开发自己的PSR-3 logger,赶紧收手,好好考虑一下你这样浪费时间是否明智。我强烈不建议你去开发自己的logger,为什么?因为现在已经有了一些堪称完美的PHP logger组件可供使用。
如果你需要一个PSR-3的logger,直接使用monolog/monolog就好了。别浪费事件到处寻找了。Monolog组件全面实现了PSR-3接口,而且它非常易于使用自定义消息格式化工具和handlers进行扩展。Monolog的消息handlers允许你将日志消息发送到文本文件、syslog、邮箱、HipChat、Slack、网络服务器、远程API、数据库以及任何你可以想象的地方。在某种极端的情况下,如果Monolog无法提供一个handler来满足你奇葩的输出目标,开发一个你自己的Monolog消息handler并集成到Monolog中也是非常简单的。例子 3-1 演示了设置Monolog并将消息记录到文本文件中是多么的简单:
例子 3-1 使用Monolog
<?phpuse Monolog\Logger;use Monolog\Handler\StreamHandler;date_default_timezone_set('America/New_York');// 准备 logger$log = new Logger('myApp');$log->pushHandler(new StreamHandler('logs/development.log', Logger::DEBUG));$log->pushHandler(new StreamHandler('logs/production.log', Logger::WARNING));// 使用 logger$log->debug('This is a debug message');$log->warning('This is a warning message');
第四个PHP-FIG的推荐标准描述了一个标准化的autoloader策略。autoloader是一个用来在运行时按需求查找PHP类、接口或者trait并将它们加载到PHP解释器中的策略。支持PSR-4 autoloader标准的PHP组件和框架可以通过一个唯一的autoloader定位并加载到PHP解释器中。很多可协同工作的组件能够关联到现代PHP生态系统中,PSR-4功不可没。
为什么autoloader这么重要
你是不是经常在你的PHP文件顶部看到这样的代码?
<?phpinclude 'path/to/file1.php';include 'path/to/file2.php';include 'path/to/file3.php';
在PHP-FIG引入PSR-4推荐标准之前,PHP组件和框架的作者们使用__autoload()和spl_autoload_register()函数来注册自定义的autoloader策略。不幸的是,每个PHP组件和框架的autoloader都是特有的,每个autoloader使用了不同的逻辑来定位和加载PHP类、接口和trait。开发者们被迫不得不在自己的PHP应用程序初始化时去调用每个组件的autoloader之后才能使用这些组件和框架。我总是喜欢使用Sensio Labs的 Twig模板组件。这个组件真的很棒。然而,如果没有PSR-4的话,我就不得不去阅读Twig的文档才能弄明白如何才能将它自定义的autoloader注册到我自己应用程序的初始化文件中,就像下面这样:
<?phprequire_once '/path/to/lib/Twig/Autoloader.php';Twig_Autoloader::register();
PSR-4 Autoloader策略
如果其它PHP的autoloader一样,PSR-4描述了一个在运行时定位和加载PHP类、接口以及trait的策略。PSR-4推荐标准并不要求你取修改自己的代码的实现。事实上,PSR-4只是给出了如何将你的代码按文件系统目录和PHP名字空间进行组织的建议。PSR-4 autoloader策略依赖于PHP名字空间和文件系统目录来定位和加载PHP类、接口和trait。
PSR-4的实质是将一个顶级的名字空间前缀映射到一个特定的文件系统目录上。例如,我可以告诉PHP,所有在\Oreilly\ModernPHP名字空间下的类、接口或者trait都存放在src/这个物理文件系统目录下。那么PHP现在就能知道,任何使用了\Oreilly\ModernPHP这个名字空间前缀的类、接口或者trait都能对应到src/目录下的子目录和文件。例如,\Oreilly\ModernPHP\Chapter1这个名字空间对应到src/Chapter1目录,而\Oreilly\ModernPHP\Chapter1\Example类对应到src/Chapter1/Example.php文件。
PSR-4允许你将一个名字空间前缀映射到一个文件系统目录上。名字空间的前缀可以是一个顶级的名字空间。名字空间前缀也可以是一个顶级的名字空间加上任意多个子名字空间。相当的灵活。
还记得我们在第二章中提到的vendor名字空间吗?PSR-4 autoloader策略与发布代码给其他开发者的组件和框架作者息息相关。一个PHP组件的代码都在一个唯一的vendor名字空间之下,组件的作者可以指定哪个文件系统目录可以与组件的vendor名字空间相对应,就像我前面演示的那样。我们将在第四章中探索这个概念。
如何开发一个PSR-4 Autoloader(以及为什么不建议你这么做)
我们知道PSR-4的兼容代码都要有一个与基本文件系统路径对应的名字空间。我们也知道名字空间前缀下的子名字空间对应着基本文件系统目录的子目录。例子3-2展示了一个从PHP-FIG网站借来的autoloader的实现,它可以用来查找并加载基于PSR-4 autoloader策略的类、接口和trait。
例子 3-2 PSR-4 autoloader
<?php/** * An example of a project-specific implementation. * * After registering this autoload function with SPL, the following line * would cause the function to attempt to load the \Foo\Bar\Baz\Qux class * from /path/to/project/src/Bar/Baz/Qux.php: * * new \Foo\Bar\Baz\Qux; * * @param string $class The fully-qualified class name. * @return void */spl_autoload_register(function ($class) { // project-specific namespace prefix $prefix = 'Foo\\'; // base directory for the namespace prefix $base_dir = __DIR__ . '/src/'; // does the class use the namespace prefix? $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { // no, move to the next registered autoloader return; } // get the relative class name $relative_class = substr($class, $len); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // if the file exists, require it if (file_exists($file)) { require $file; }});