[最新の PHP] 第 3 章 標準
PHP コンポーネントとフレームワークの数は気が遠くなるようなものです。 Symfony や Laravel のような巨大なフレームワークもあれば、Silex や Slim のようなマイクロ フレームワークもあります。 CodeIgniter など、最新の PHP コンポーネントが登場するずっと前から存在していたレガシー フレームワークもあります。最新の PHP エコシステムはまさにコードのるつぼであり、開発者が素晴らしいアプリケーションを構築するのに役立ちます。
残念ながら、これらの古い PHP フレームワークは比較的閉鎖的な環境で開発されており、他の PHP フレームワークとコードを共有できません。 。プロジェクトで古い PHP フレームワークを使用している場合、フレームワークにしっかりと閉じ込められ、フレームワーク自体のエコシステムに囚われて抜け出すことができなくなります。フレームワーク自体が提供するツールに満足している場合は、この比較的集中化された環境で問題はありません。ただし、CodeIgniter フレームワークを使用しており、Symfony フレームワークからいくつかの優れたクラス ライブラリを選択して使用したい場合は、プロジェクト専用のワンタイム アダプターを作成しない限り、それほど幸運ではないかもしれません。
ここで得られたものはコミュニケーションの失敗です コミュニケーション)
-- 映画『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-FIG は、PHP フレームワーク プロジェクトの代表者グループによって設立された組織です。そのメンバーは選挙で選ばれたものではなく、PHP コミュニティを発展させたいという彼らの願望以外に特別な点はありません。どなたでも会員登録が可能です。提案段階にある PHP-FIG 推奨事項については、誰でもフィードバックを送信できます。結果として得られる PHP-FIG 推奨事項は、多くの場合、最大かつ最も人気のある PHP フレームワークの多くで採用され、実装されます。 PHP-FIG に参加することを強くお勧めします。フィードバックを送信して、お気に入りの PHP フレームワークの の未来を一緒に構築するのにご協力ください。
PHP-FIG はさまざまな推奨事項を提供する責任があるとよく言われますが、推奨事項はルールと同等ではなく、推奨事項 を実装する必要はないため、これを理解することが非常に重要です。 。これらの推奨事項は慎重に検討され、慎重に作成された提案であり、PHP 開発者 (さらには PHP フレームワーク作成者) にさらなる利便性をもたらすことができます。
フレームワーク連携
PHP-FIG の使命は、フレームワークの連携を実現することです。フレームワークのコラボレーションとは、インターフェイス、自動読み込み、コーディング スタイルを通じて連携することを意味します。
インターフェース
PHP フレームワークは、共有インターフェイスを通じて連携します。 PHP インターフェイスを介して、フレームワークは、サードパーティの依存ライブラリがこれらのインターフェイスをどのように実装するかを考慮せずに、サードパーティの依存ライブラリによってどのメソッドが提供されているかを確認できます。
PHP インターフェースの詳細な説明については、第 2 章を参照してください。
たとえば、特定の PHP フレームワークがサードパーティのロガー オブジェクトを喜んで共有しているとします。 、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 標準勧告の略です。最近 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 を理解するための前提条件です。
PSR-1: 基本コーディングスタイル
(必要に応じて)作成するコードは、PSR-1 以降の PHP コミュニティ標準と互換性のあるものにしてください。これは最も簡単に使用できる標準です。非常にシンプルなので、すでにこれらの標準を使用していることに気づかないかもしれません。 PSR-1 は、それほど労力をかけずに実装できるシンプルなガイドラインを提供します。 PSR-1 は、PHP フレームワークに組み込むことができるベースラインのコーディング スタイルを提供するように設計されています。 PSR-1 標準と互換性を保つには、次の要件を満たす必要があります:
PHP タグ
または = ?> タグの間に PHP コードを含める必要があります。他の PHP タグ構文は使用できません。
文字エンコーディング
すべて PHPファイルはバイト オーダー マーク (BOM) なしで UTF-8 でエンコードする必要があります。これは少し面倒に思えますが、ほとんどのエディターまたは IDE は
ファイルの使用状況
別の PHP ファイルで、その中で宣言 (クラス、特性、関数、定数など) を定義するか、結果を生成する操作を実行します (たとえば、output情報を取得するか、データを変更します)。しかし、両方を行うことはできません。これは簡単な作業であり、少しの先見性と計画が必要なだけです。
自動読み込み
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-2 スタイルに自動的にフォーマットできるツールがいくつかあります。
PSR-1 実装の前提条件
PSR-2 コーディング スタイルはい、まず PSR-1 コーディング スタイル
Indentation
これは両陣営の間で話題になっている。一方の当事者は、コードのインデントにタブ文字を使用することを好みます。反対側 (よりクールな側) は、コードのインデントを実現するためにいくつかのスペースを使用することを好みます。 PSR-2 勧告では、PHP コードは 4 つのスペース
を使用してインデントする必要があると述べられています。個人的な経験によれば、スペース スペースは異なるコード エディターでもほとんど常に同じ幅であるため、文字はインデントに適しています。タブ文字の幅はコード エディタによって異なります。 4 つのスペース文字を使用してコードをインデントすると、コードの視覚的な一貫性が最大限に保たれます。 PHP ファイルでは、各行の末尾に Unix 改行 (LF) を使用する必要があります。ファイルの末尾に ?> タグを使用することはできません。コードの各行は 80 文字以下にする必要があります。たとえ基準が緩和されたとしても、最終的にはコードの各行は 120 文字を超えることはできません。各行の末尾に余分なスペースを含めることはできません。これは大変な作業のように思えますが、重要なことです。ほとんどのコード エディターは、コードを幅に合わせて自動的に折り返したり、末尾の null 文字を除外したり、Unix 改行を使用したりできます。これらの問題はすべて、毎回考える必要がなく、自動的に処理される必要があります。
キーワード
の後には各名前空間宣言が必要です 空行。同様に、 use キーワードを使用してネームスペースまたはそのエイリアスをインポートする場合は、 use ステートメントの後に空行を追加する必要があります。次の例を参照してください:
クラス
和缩进一样,类定义中的括号位置也是引发另一个强烈争议的话题。有些人喜欢将开始括号放在类名后的同一行中,而另一些人更喜欢另起一行,将开始括号放在类名后新的一行中。PSR-2推荐指出,类定义的开始括号必须像下面的例子中一样放在类名的定义语句后的新的一行中。类定义的结束括号必须放置在类定义体末尾之后新的一行中。或许你早就按照我们说的做了,所以没什么大不了的。如果你的类继承自其它的类或者实现了其它的接口,extends和implements关键词必须与类名保持在统一行中:
<?php namespace My\App;class Administrator extends User{ // 类的定义体}
方法
方法定义中的括号位置与类定义的括号位置一样。方法定义的开始括号放到方法名之后新的一行中。方法定义的结束括号放到方法定义体末尾的新的一行中。注意方法的参数。第一个圆括号之后不可以有空格,而最的圆括号之前也不可以有空格。每个方法的参数(除了最后一个)后都紧跟着一个逗号和一个空格:
<?phpnamespace Animals;class StrawNeckedIbis{ public function flapWings($numberOfTimes = 3, $speed = 'fast') { // 方法定义体 }}
你必须给每个类的属性和方法声明一个作用域。作用域是public、protected或者private之一,作用域决定了一个属性或者方法在类的内部或者外部是否可以被访问。老派的PHP开发者们可能习惯了在类的属性前加上var关键词以及在私有方法前加上下划线_。千万别学他们这么做,你必须使用前面介绍的三个作用域之一。如果你需要将一个类的属性或者方法声明为abstract或者final,那么abstract和final修饰符必须放到作用域之前。如果你将一个属性或者方法声明为静态的,static修饰符必须放在作用域之后:
<?phpnamespace Animals;class StrawNeckedIbis{ // 设置了作用域的静态属性 public static $numberOfBirds = 0; // 设置了作用域的方法 public function __construct() { static::$numberOfBirds++; }}
这可能是我最容易犯错的指导原则。所有的控制结构的关键词都必须跟随一个单独的空字符。控制结构关键词包括了:if、elseif、else、switch、case、while、do while、for、foreach、try、catch。如果控制结构关键词需要使用一对圆括号,请确保第一个圆括号后面不要加空格,以及最后一个圆括号前面不要加空格。与类和方法的定义不同,开始括号必须放在控制结构关键词之后,并保持与控制结构关键词在同一行中。控制结构关键词的结束括号必须另起一行。下面用一个简单的例子来说明这些指导原则:
<?php$gorilla = new \Animals\Gorilla;$ibis = new \Animals\StrawNeckedIbis;if ($gorilla->isAwake() === true) { do { $gorilla->beatChest(); } while ($ibis->isAsleep() === true); $ibis->flyAway();}
你还可以使用Fabien Potencier的PHP-CS-Fixer来自动修正绝大多数的代码不兼容性。这个工具虽然还不够完美,但是有了它你只需要花很少的精力就可以保证尽可能的遵循PSR兼容性。
PSR-3:Logger接口
第三个PHP-FIG的推荐标准并不像前面那些一样,它不是一组指导原则。PSR-3是一个接口,它指定了可以用来实现PHP logger组件的方法。
一个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-4autoloader标准的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; }});