新しいPHPパート1:戻り値の型

PHPのメジャーリリースごとに、いくつかの新しい機能が追加されていますが、その一部は本当に重要です。 PHP 5.3の場合、これらは名前空間と匿名関数でした。 PHP 5.4の場合-特性。 PHP 5.5の場合-ジェネレーター。 5.6の場合-可変長の引数のリスト。

PHP 7には、開発者の作業を楽にする多数の革新と改善があります。 しかし、最も重要で永続的な変更は型を扱うことだと思います。 新機能の組み合わせにより、PHP開発の見栄えが良くなります。

強力なタイピングサポートがなぜそれほど重要なのですか? これは、プログラムを提供します-コンパイラーまたはランタイム、および他の開発者は、コードを実行することなく、あなたがしようとしたことに関する貴重な情報を提供します。 これには、次の3つのタイプの利点があります。

  1. 他の開発者にコードの目的を伝えるのがずっと簡単になっています。 それはほとんどドキュメンテーションのようなもので、ただ良いだけです!
  2. 厳密な型指定により、コードの動作に焦点が絞られるため、分離性が向上します。
  3. プログラムは、人のように厳密なタイピングを読み取り、理解します。コードを分析し、エラーを見つけることが可能になります...実行する前に!

ほとんどの場合、明示的に型付けされたコードは理解しやすく、弱く型付けされたコードよりも構造が良く、エラーが少ないです。 ごく一部のケースでのみ、厳密な型指定は良いというよりも問題ですが、心配する必要はありません。PHPの型チェックは以前と同様にオプションです。 しかし実際には、常に使用する必要があります。

戻り型


既存の型付けシステムへの最初の追加は、戻り値型のサポートです。 これで、関数とメソッドで戻り値の型を明示的な形式で指定できます。 次の例を考えてみましょう。

class Address {  protected $street;  protected $city;  protected $state;  protected $zip;  public function __construct($street, $city, $state, $zip) {    $this->street = $street;    $this->city = $city;    $this->state = $state;    $this->zip = $zip;  }  public function getStreet() { return $this->street; }  public function getCity() { return $this->city; }  public function getState() { return $this->state; }  public function getZip() { return $this->zip; } } 

 class Employee { protected $address; public function __construct(Address $address) {   $this->address = $address; } public function getAddress() : Address {   return $this->address; } } 

 $a = new Address('123 Main St.', 'Chicago', 'IL', '60614'); $e = new Employee($a); print $e->getAddress()->getStreet() . PHP_EOL; // Prints 123 Main St. 

このかなり一般的な例では、渡した電子メールアドレスを含むプロパティが1つだけのEmployeeオブジェクトがあります。 getAddress()メソッドに注意してください。 関数のパラメーターの後に、コロンと型があります。 戻り値を取ることができる唯一のタイプです。

戻り値の型の後置構文は、C / C ++またはJavaに慣れている開発者にとっては奇妙に思えるかもしれません。 ただし、実際には、プレフィックス宣言アプローチはPHPには適していません。 関数名の前には多くのキーワードがあります。 パーサーの問題を回避するために、PHPはGo、Rust、およびScalaに類似したパスを選択しました。

getAddress()によって他の型がgetAddress()れるとgetAddress() PHPはTypeError例外をスローします。 nullでも型の要件を満たしません。 これにより、 Addressオブジェクトのprintメソッドに絶対的な信頼性でアクセスできます。 この特定のタイプのオブジェクトが実際に返すのは、 nullではなく、falseではなく、文字列や他のオブジェクトではないことを確実に知ることができnull 。 これにより、作業の安全性と追加のチェックが不要になり、コードがよりクリーンになります。 何かがうまくいかなくても、PHPは間違いなく警告します。

しかし、それほど重要ではないケースがあり、 Addressオブジェクトが存在しない状況を処理する必要がある場合はどうでしょうか。 EmployeeRepositoryを導入します。このロジックにより、エントリを作成できなくなります。 まず、IDフィールドをEmployeeクラスに追加します。

 class Employee { protected $id; protected $address; public function __construct($id, Address $address) { $this->id = $id; $this->address = $address; } public function getAddress() : Address { return $this->address; } } 

次に、リポジトリを作成します。 (スタブとして、ダミーデータをコンストラクタに直接追加しますが、実際にはもちろん、リポジトリのデータソースが必要です)。

 class EmployeeRepository { private $data = []; public function __construct() { $this->data[123] = new Employee(123, new Address('123 Main St.', 'Chicago', 'IL', '60614')); $this->data[456] = new Employee(456, new Address('45 Hull St', 'Boston', 'MA', '02113')); } public function findById($id) : Employee { return $this->data[$id]; } } 

 $r = new EmployeeRepository(); print $r->findById(123)->getAddress()->getStreet() . PHP_EOL; 

ほとんどの読者は、 `findById()`にバグがあることにすぐに気付くでしょう。 存在しない従業員IDを要求する場合、PHPは `null`を返し、` getAddress()への呼び出しは `non-objectで呼び出されたエラー"メソッドで死にます。 しかし、実際にはエラーはありません。 それは、 `findById()`が従業員を返さなければならないという事実にあります。 エラーが誰であるかが明確になるように、 `Employee`戻り値の型を指定します。

本当にそのような従業員がいない場合はどうしますか? 2つのオプションがあります。1つ目は例外です。 約束したものを返せない場合-これは、通常のコードフロー以外の特別な処理の機会です。 もう1つは、インターフェイスの表示であり、その実装が返されます(「空」を含む)。 したがって、残りのコードは機能し、「空の」ケースで何が起こっているかを制御できます。

アプローチの選択はユースケースに依存し、正しいタイプを返すことができない場合の結果の容認できないフレームワークによっても決定されます。 リポジトリの場合、例外を支持する選択を主張します。そのような状況に陥る可能性は最小限であり、例外を処理することはパフォーマンスの点で非常に高価だからです。 スカラー変数を扱っている場合、「空の値」を処理することは受け入れられる選択肢です。 それに応じてコードを変更します(簡潔にするため、変更された部分のみを示しています)。

 interface AddressInterface { public function getStreet(); public function getCity(); public function getState(); public function getZip(); } 

 class EmptyAddress implements AddressInterface { public function getStreet() { return ''; } public function getCity() { return ''; } public function getState() { return ''; } public function getZip() { return ''; } } 

 class Address implements AddressInterface { // ... } 

 class Employee { // ... public function getAddress() : AddressInterface { return $this->address; } } 

 class EmployeeRepository { // ... public function findById($id) : Employee { if (!isset($this->data[$id])) { throw new InvalidArgumentException('No such Employee: ' . $id); } return $this->data[$id]; } } 

 try { print $r->findById(123)->getAddress()->getStreet() . PHP_EOL; print $r->findById(789)->getAddress()->getStreet() . PHP_EOL; } catch (InvalidArgumentException $e) { print $e->getMessage() . PHP_EOL; } /* * Prints: * 123 Main St. * No such Employee: 789 */ 

getStreet()は、素敵な空の値を提供します。

戻り値の型に関する重要な注意事項:継承中、型は変更できず、より具体的にすることさえできません(たとえば、サブクラス)。 その理由は、遅延読み込みPHPの機能です。

戻り値の型は大きいですが、PHP型システムを拡張する唯一の新機能ではありません。 2番目の部分では、別の、おそらくさらに重要な変更を検討します。スカラー型の宣言です。

Source: https://habr.com/ru/post/J267257/


All Articles