(jp) =
#今まで…
…実際には単なる配列以上の配列を PHP で操作したことがありますか? 配列キーをフィールドとして使用しましたか? そして、その配列に何が入っているのか正確にわからないという苦痛を感じましたか? その中のデータが実際に期待どおりのものであるかどうか、またはどのフィールドが利用可能か確信が持てませんか?
私が話していることを視覚化しましょう:
$line =
import($line['id'], $line['name'], $line['amount']);
別の例: 検証済みのリクエスト データはどうでしょうか?
function store(PostRequest $request, Post $post)
$data = $request->validated();
$post->title = $data['title'];
$post->author_id = $data['author_id'];
PHP の配列は、強力で用途の広いデータ構造です。 しかし、ある時点で、彼らの問題に対するより良い解決策があるかどうか疑問に思うはずです.
#自分が何を書いているかを知る
このブログの定期的な読者は、私が過去に型理論について書いたことを知っているかもしれません。 強力な型システムの長所と短所については、ここでは触れません。 しかし、配列がリスト以外のものとして使用されることを意図している場合、配列はひどい選択であると言いたいです。
簡単な質問があります: この配列には何が入っていますか?
function doSomething(array $blogPost)
$blogPost[];
この場合、どのデータを扱っているかを知る方法はいくつかあります。
- ソースコードを読んでください。
- ドキュメントを読んでください。
- ごみ
$blogPost
それを検査します。 - または、デバッガーを使用して検査します。
私は単にこのデータを使用したかったのですが、次にわかったのは、実際にどのような種類のデータを扱っているかをデバッグすることです。 これらは本当にプログラマーが注目すべきことでしょうか?
この不確実性を排除することで、認知負荷を大幅に軽減できます。 これは、アプリケーションやビジネス ロジックなど、本当に重要なことに集中できることを意味します。 ほら、それはほとんどのクライアントがあなたにお金を払っていることです。
厳密に型指定されたシステムは、正確に何を扱っているかを理解するのに非常に役立ちます。 たとえば、Rust のような言語は、この問題をきれいに解決します。
struct BlogPost
title: String,
body: String,
active: bool,
構造体が必要です! 残念ながら、PHP には構造体がありません。 配列とオブジェクトがあり、それだけです。
しかし、私たちは できる 次のようにします。
class BlogPost
public string $title;
public string $body;
public bool $active;
ちょっと待ってください。 私たちは本当にこれを行うことはできません。 まだ. PHP 7.4 では型付きプロパティが追加されますが、まだ先の話です。
型付きプロパティは既にサポートされていますが、ちょっと想像してみてください。 前の例を次のように使用すると、IDE がオートコンプリートできます。
function doSomething(BlogPost $blogPost)
$blogPost->title;
$blogPost->body;
$blogPost->active;
関係をサポートすることさえできます:
class BlogPost
public Author $author;
function doSomething(BlogPost $blogPost)
$blogPost->author->name;
私たちの IDE は、私たちが扱っているデータをいつでも教えてくれます。 しかしもちろん、型付きプロパティは PHP にはまだ存在しません。 存在するのは… docblock です。
class BlogPost
public $title;
public $body;
public $active;
public $author;
ただし、Docblock はちょっと混乱しています。非常に冗長で醜いです。 しかし、もっと重要なことは、データが彼らが言うタイプであるという保証を与えていないということです!
幸いなことに、PHP にはリフレクション API があります。 これにより、今日でもさらに多くのことが可能になります。 上記の例は、プロパティに直接書き込まない限り、実際には少しのリフレクション マジックで型検証できます。
$blogPost = new BlogPost([
'title' => 'First',
'body' => 'Lorem ipsum',
'active' => false,
'author' => new Author()
]);
それはかなりのオーバーヘッドのようですね。 ただし、最初の例を思い出してください。 これらのオブジェクトを手動で構築しようとしているのではなく、CSV ファイル、リクエスト、または他の場所からそれらを読み取っています。
$blogPost = new BlogPost($line);
悪くないですよね? そして覚えておいてください: 少しのリフレクション マジックは、値が正しい型であることを保証します。 それがどのように機能するかは後で説明します。
私はこのアプローチを好みます。 さもなければブラックボックスになるであろうもののオートコンプリートを有効にします。 もう少しセットアップが必要ですが、データの定義を書く必要があります。 長期的なメリットはそれだけの価値があります。
補足: 「長期的には」と言うとき、このアプローチは、複数の開発者が同じコード ベースで長期間にわたって作業している大規模なプロジェクトで特に役立つことを意味します。
# 反映型
では、プロパティが正しい型であることをどのように保証できるのでしょうか? シンプル: を読む @var
docblock 宣言、その型に対して値を検証してから、それを設定します。 値の型が間違っている場合は、単純に TypeError
.
この追加のチェックを行うことは、プロパティに直接書き込むことができないことを意味します。 少なくとも、それらが公開されていると宣言されている場合はそうではありません。 私たちの場合、パブリック プロパティは、これらのオブジェクトをいつ使用するかという理由から、本当に必要なものです。 それらからデータを簡単に読み取れるようにしたいと考えています。 オブジェクトが構築された後にそれらに書き込むべきではないため、書き込みを簡単にすることはあまり気にしません。
そのため、値を設定する前に、その型に対して値を検証するための「フック」が必要です。 PHP でこれを行うには 2 つの方法があります。 実際にはもっとありますが、これらの2つは関連しています。
#マジックセッター付き
プライベートまたは保護されたプロパティと組み合わせた魔法のセッターを使用すると、値を設定する前に型の検証を実行できます。
ただし、前に述べたように、クリーンでパブリックな API から読み取る必要があります。 残念ながら、魔法のセッターはダメです。
# コンストラクタ経由
前の例のように、データの配列をコンストラクターに渡すと、コンストラクターはそのデータをそのクラスのプロパティにマップします。 これが進むべき道です。
これを行う簡単な方法を次に示します。
public function __construct(array $parameters)
$publicProperties = $this->getPublicProperties();
foreach ($publicProperties as $property)
$value = $parameters[$property->getName()];
if (! $this->isValidType($property, $value)
throw new TypeError("…");
$this->$property->getName() = $value;
多分あなたは何について興味がありますか isValidType
正確には? これも単純化された実装です。
protected function isValidType(ReflectionProperty $property, $value): bool
}
もちろん、ここにはいくつか欠けているものがあります。
- 組合の種類:
@var string|int
-
@var mixed
サポート - ジェネリック コレクション:
@var \Foo[]
- Null 許容のサポート:
@var int|null
しかし、これらのチェックを私たちの isValidType
方法。 ちなみに、それはまさに私たちが行ったことであり、これをパッケージ化しました: spatie/data-transfer-object.
# 不変性はどうですか?
不変性をどのように処理するかは、答える最後の質問です。 これらのオブジェクトを使用して外部からのデータを表す場合、これらのオブジェクトが構築された後にこれらのオブジェクトを変更するための有効なユースケースはありますか?
98% のケースで、答えは単純明快です: いいえ。 データ ソースを変更することは決してできないため、そのソースを表すオブジェクトを変更することはできません。
実際のプロジェクトは、ここで説明するほど白黒ではないことがよくあります。 ユースケースもあるかもしれませんが、「一度作ったら変わらない」という考え方は良いと思います。
では、これを PHP で強制するにはどうすればよいでしょうか。
残念ながら、そうではありません。 PHP のコアには、いわゆる「読み取り専用」プロパティの話がありますが、正しく理解するのは困難です。 では、ユーザーランド型システムはどうでしょうか? 読みやすさをあきらめていない限り、オートコンプリート部分。 PHP でこの目標を達成する方法はありません。
ほら、私たち 必要 この動作をサポートする魔法のゲッター。 同時に私たちは しないでください それらが欲しい。 それらは、私たちが達成しようとしている目標の 1 つである簡単な発見可能性を否定することになります。
現時点では、残念ながら、パッケージはオブジェクトの構築後にオブジェクトのプロパティへの書き込みを許可します。 私たちはそれをしないように注意しています。
この投稿があなた自身のコード ベースについて考えるきっかけになり、プロジェクトでこのパターンを試すよう促されることを願っています。 私たちのパッケージまたは独自の実装で。
何か考えが浮かんだり、これについてさらに話し合いたい場合は、ぜひここに連絡してください! あなたは私に連絡することができます ツイッター または電子メール。