この記事では、PHP のジェネリックについて説明し、ジェネリックに関する基本的な知識を紹介します。
PHP のジェネリックス。これが私が望んでいることだとわかっていました。多くの開発者がこのタイプを使用することを望んでいることはわかっています。一方で、ジェネリックとは何か、またはなぜこの型があるのかを知らない PHP プログラマーも多数いるかもしれません。
このブログではジェネリックとPHPに関するシリーズを行う予定です。まずは最初から始めて、すぐにより複雑なトピックに移ります。ジェネリックとは何か、PHP がジェネリックをサポートしない理由、そして将来何が起こるかについて説明します。
始めましょう。
すべてのプログラミング言語には何らかのシステムがあります。一部の言語は実装が非常に厳密ですが、他の言語 (PHP はこのカテゴリに分類されます) ははるかに緩やかです
さて、型システムを使用する理由はたくさんあります。 最も明白なのは、型検証です。
2 つの数値、2 つの整数を受け取り、それらに対して何らかの計算を行う関数があるとします。
function add($a, $b) { return $a + $b; }
PHP では、数値、文字列、ブール値など、あらゆる種類のデータを関数に渡すことができます。 PHP は、変数を加算するなど、意味がある場合には変数を変換するために最善を尽くします。
add('1', '2');
しかし、これらの変換 (型のジャグリング) は、予期しない結果、つまりエラーやクラッシュを引き起こすことがよくあります。
add([], true); // ?
これで、数学的加算演算が任意の入力に使用されることを確認するコードを手動で作成できます
function add($a, $b) { if (!is_int($a) || !is_int($b)) { return null; } return $a + $b; }
あるいは、PHPS 組み込みの型ヒントを使用することもできます。これは、私たちが行うための組み込みの短縮表現です。操作は手動で:
function add(int $a, int $b): int { return $a + $b; }
PHP コミュニティの多くの開発者は、この関数には整数だけを渡すべきであることを知っているため、これらの型ヒントについてはあまり気にしていないと述べています。結局のところ、この関数は自分たちで作成したのです。
しかし、この推論はすぐに破綻します。多くの場合、そのコードベースで作業しているのはあなただけではなく、自分で書いたわけではないコードを使用していることになります。Composer でどれだけのパッケージを取り込むかを考えてみてください。したがって、この個別の例は大したことのようには見えませんが、コードが大きくなり始めると、型チェックが役に立ちます。
さらに、型ヒントを追加すると、無効な状態を防ぐだけでなく、プログラマが必要とする値の入力の種類も明確になります。関数の機能のほとんどはすでにその型定義によってカプセル化されているため、型を定義すると、通常、外部ドキュメントを読む必要がなくなります。
IDE はこの原則を多用します。つまり、関数が期待する値の入力の種類、またはオブジェクトがクラスに属しているため、オブジェクトで使用できるフィールドとメソッドをプログラマに伝えることができます。 IDE を使用すると、コード ベースの型ヒントを静的に分析できるため、コードをより効率的に作成できるようになります。
この言葉を覚えておいてください: 静的分析 - これはこのシリーズの後半で非常に重要になります。 これは、プログラム、IDE、または他のタイプの「静的アナライザー」がコードを調べて、実行しなくても動作するかどうかを、少なくともある程度は知らせることができることを意味します。整数のみを受け入れる文字列を関数に渡すと、IDE は何が間違っているのかを教えてくれます。これにより、実行時にプログラムがクラッシュしますが、IDE は実際にコードを実行しなくても教えてくれます。
その一方で、型システムにも限界があります。 一般的な例は「項目のリスト」です。
class Collection extends ArrayObject { public function offsetGet(mixed $key): mixed { /* … */ } public function filter(Closure $fn): self { /* … */ } public function map(Closure $fn): self { /* … */ } }
コレクションには、ループ、フィルタリング、マッピングなど、あらゆるタイプの入力を処理するための多くの方法があります。コレクションの実装では、文字列を処理するか整数を処理するかを気にする必要はありません。
しかし、外部の視点から見てみましょう。あるコレクションには文字列のみが含まれ、別のコレクションには「ユーザー」オブジェクトのみが含まれるようにしたい場合はどうなるでしょうか。コレクション自体はその項目をループするときに気にしませんが、私たちは気にします。ループ内の項目がユーザーなのか文字列なのかを知りたいのですが、それはまったく異なります。ただし、正しい型情報がないと、IDE は未知の状況で実行されます。
$users = new Collection(); // … foreach ($users as $user) { $user-> // ? }
これで、コレクションごとに個別の実装を作成できます。1 つは文字列でのみ機能し、もう 1 つは User オブジェクトでのみ機能します:
class StringCollection extends Collection { public function offsetGet(mixed $key): string { /* … */ } } class UserCollection extends Collection { public function offsetGet(mixed $key): User { /* … */ } }
しかし、3 番目の実装が必要な場合はどうすればよいでしょうか?第4?たぶん10か20。これらのコードの管理は非常に困難になります。
ここでジェネリック医薬品が登場します。
明確にしておきます: PHP にはジェネリックがありません。これはかなりの回り道を伴う大胆な発言ですが、これについてはこのシリーズの後半で説明します。しかし今、私がこれから紹介するものは PHP には存在しないと言えます。 しかし、他のプログラミング言語にも存在します。
多くのプログラミング言語では、開発者が考えられる型ごとに個別の実装を実装するのではなく、コレクション クラスで「ジェネリック」を定義できます。
class Collection<Type> extends ArrayObject { public function offsetGet(mixed $key): Type { /* … */ } // … }
基本上我们说的是集合类的实现适用于任何类型的输入,但是当我们创建集合的实例时,我们应该指定一个类型。它是一个泛型实现,需要根据程序员的需求来特定:
$users = new Collection<User>(); $slugs = new Collection<string>();
添加类型似乎是一件小事。但这种类型本身就开启了一个充满可能性的世界。 我们的 IDE 现在知道了集合中的数据类型,它可以告诉我们是否添加了错误类型的项;它可以告诉我们在迭代集合时可以对项执行什么操作;它可以告诉我们是否将集合传递给知道如何处理这些特定项的函数。
虽然我们可以通过手动为我们需要的每种类型实现一个集合,在技术上实现同样的效果;对于编写和维护代码的开发人员来说,通用实现将是一项重大改进。
那么,我们为什么不在 PHP 中使用泛型呢?除了无聊的收藏,我们还能用它们做什么?我们能为他们增加支持吗?我们将在这个系列中回答所有这些问题。首先需要澄清的是:我在本系列文章中的目标是教你关于泛型的知识,但同样重要的是,我想让大家意识到我们是如何误解 PHP 的。我想改变这种状况。
推荐学习:《PHP视频教程》