$ _POST、$ _ GETなどはカプセル化の原則に違反していますか?


9

グローバルを使用すると、コードのテストが困難になり、バグが発生しやすくなり、安全で予測不可能になります。そのため、関数/オブジェクト内で必要な変数を渡します。だから私の質問は簡単です:

$ _POST、$ _ GETなどはカプセル化の原則に違反していますか?

これらの変数の制御をオブジェクト指向の方法で維持するには、理想的な解決策は次のような行をコードに追加することだと思います。

// Convert the $_GET array to an object
$get = json_decode(json_encode($_GET), FALSE);  // stackoverflow.com/a/1869147
// Stop it from being included from anywhere
unset($_GET);

// Small example of what could be done later on
$DB = new PDO(/* ... */);
$Person = new Person($DB, $get->id);

これはどこにも見たことがなく、チュートリアルも推奨もしていません。また、上記のコードは、モックオブジェクトを使用できるため、インクルードしたコード$Person = new Person($DB, $_GET['id']);や(醜い)コードよりもはるかにテストが簡単であることを明確に確認$Person = new Person($DB);でき$getます。

上記のコードは正しい方向にありますか、それとも何か不足していますか?

編集:アレクサンダークズミンが示唆したようにいくつかの調査(ZendフレームワークCake PHP)の後、それは行くのが正しいようです。それらはおそらく私がコードATMを掘り下げるには大きすぎるが、私はそれを覚えておきます。


1
カプセル化は常に「原則」として固執するのはばかげているように見えました。これは単なる機能です。言語が持っているか、持っていないかのどちらかです。
Ignacio Vazquez-Abrams

私はネイティブではないので、それをどのように呼ぶかわからず、原則を書きました。より良い言い回しがあると思われる場合は、タイトル/テキストを自由に編集してください。
Francisco Presencia 2013年

6
これはスマートで、ほとんどのPHPフレームワークが解決する方法でもあります。ええ、私はあなたが何かに夢中になっていると思います。
Alexander Kuzmin 2013年

@AlexanderKuzmin!更新された最高のようです。
Francisco Presencia 2013年

なぜオピニオンベースの終了タグがあるのですか?2番目のサブ質問は意見ベースであることに同意します(ただし、特定の例についてのみ)が、主な質問は有効だと思います。
Francisco Presencia 2013年

回答:


8

なぜ「配列に変換」するのに適用json_decodeするのかよくわかりません$_GET$_GET既にある配列。

スーパーグローバル(使用$_GET$_POSTなど)があるカプセル化の原則に違反します。しかし、物をカプセル化するのをやめるところに線が引かれているはずです。要求データはカプセル化の良い候補ですが、すべてのものカプセル化しようとするといううさぎの穴に吸い込まれないでください。

ほとんどのフレームワークは通常、PHPのスーパーグローバルを何らかの形式のリクエストオブジェクトにラップします。これを行うと、テストなどのモックが作成しやすくなります。最も簡単な方法は次のとおりです。

<?php
class Request
{
    public $get;
    public $post;
    public $session;
    public $cookie;

    public function __construct($get, $post, $session, $cookie)
    {
        $this->get = $get;
        $this->post = $post;
        $this->session = $session;
        $this->cookie = $cookie;
    }
}

$request = new Request($_GET, $_POST, $_SESSION, $_COOKIE);

それはシンプルで初歩的ですが、仕事をします。また、この時点でデータをフィルタリングして、XSSインジェクションから保護することをお勧めします。

しかし、それはRequestオブジェクトに包まれています。Requestオブジェクトは、4つの配列を有し、そしてこれらの配列を容易にモックすることができます。

$get = array(
    'foo' => 'bar'
);
$post = array();
$session = array(
    'user' => 1
);
$cookie = array();

$request = new Request($get, $post, $session, $cookie);

コメントに記載されているように、それらをオブジェクトに変換しようとしています// Convert the $_GET array to an object。さらに、この答えstackoverflow.com/a/1869147が私がそれをしている理由です。その細かい細部は別にして、私が意図したものと非常によく似ている、追加のヒントを含む完全な回答をありがとうございました。
Francisco Presencia 2013年

3
$_GETデータをオブジェクトに変換しません。配列はダーティではありません。<table>表形式のデータであってもセマンティックではないことを恐れてHTMLでだれも敢えて使用しないように、人々は配列を「OOPではない」と思うので恥ずかしがります。$_GETアレイを取ります。私は私のフォームから配列データを渡す、つまり場合はどうなります<input type="checkbox" name="foo[]" /><select name="bar[]" multiple="multiple">?それらをオブジェクトに変換しますか、それともそのままにしますか?ただ、去る$_GET意図したように、配列として配列を。
Martin Bean

複数レベルの配列の場合についても考え、サブオブジェクトにすることを想定しましたが、仮定は適切ではありません。これを(これまではダーティ)オブジェクトに変換して、ゲッターとセッターを変更できるようにしました。これにより、たとえば出力のXSSインジェクションをチェックする柔軟性が高まりますが、これは提案されたスキームでも実行できます。
Francisco Presencia 2013年

ええ、あなたはそれらを多次元配列のままにしておくのも良いでしょう。配列はダーティではなく、データ構造として使用しても、コードは「非OOP」にはなりません。それらを怖がらないでください。あなたのデータは、(のような任意のメソッドやプロパティがない場合GETPOSTデータを)それはおそらくオブジェクトである必要はありません。
Martin Bean

PHPの$ _SESSIONスーパーグローバルの特別な処理のために、リクエストオブジェクトに$ _SESSIONを含める決定をクエリします(ゲッター/セッターを実装して、セッションは早期に開始されますなど)、それは実際には「リクエスト」IMOの一部ではありません。
MrWhite、2014年

2

スーパーグローバル$_{POST,GET,SERVER}などを使用すると、カプセル化に確実に違反します。

この問題は、現在多くのフレームワークで行われているように、アプリケーションのサーバー側で「ローカルリクエスト」を作成したいときに大きくなります。

私はフレームワークでの作業に慣れていませんが、通常行うことは、処理の最初に要求/応答ペアを作成することです。リクエストには、これらのグローバルパラメータの値が含まれています。

サーバー側のサブリクエストを作成する場合、2つのオプションがあります。現在のコンテキストを使用するか、まったく新しいコンテキストを作成します。したがって、これらのスーパーグローバル変数の設定を解除しないでください。これらを再び使用する可能性があるからです。また、このため、リクエストパラメータはシングルトンである必要があります。

これらのスーパーグローバルへの参照ではなく、値のみを含めることにより、1つのRequestオブジェクトの変更が別のRequestオブジェクトに影響を与えることはないため、グローバル状態の問題は解決されます。

つまり、基本的には、2つのオプションがあります。

// Using global context
$request = new Request(array(
    'post' => $_POST,
    'get' => $_GET
));

// or creating a new context

$request = new Request(array(
    'post' => ['someKey' => 'someValue'],
    'get' => ['queryParam' => 'queryValue'],
));

0

POST-およびGET-varsは1つの一括でサーバーに送信され、phpはそれらを理解する必要があります。ある意味では、それらをグローバルに利用できるようにすることは理にかなっており、開発者はそれらをどこで処理するかを選択できます。

多くのフレームワーク(CakePHPなど)はパラメーターを読み取り、それらをすべて配列、オブジェクト、または類似の構造に配置します。その後、他のデータと同様に処理され、それらを必要とするすべてのメソッドに渡されます。


1
PHPは次のよ​​うに現在のメインスクリプトにローカル変数を挿入できないため、このように機能しindex.phpますか?
Francisco Presencia 2013年

いいえ。ただし、グローバル配列を介して変数を提供すると、開発者が何らかの形式の解析を実装する際の柔軟性が高まります。

0

カプセル化は、何かの複数のインスタンスが必要になる可能性がある場合に適した原則です。ただし、Webページには1セットのパラメーターしかありません。グローバル変数ではなくクラスに属している場合、それらはおそらくシングルトンになります。グローバル変数からシングルトンクラスへの大幅な改善はありません。アクセスするための構文は異なります。これらは依然として本質的にグローバルオブジェクトですが、クラスインスタンスのハンドルを取得して渡す必要があるため、より面倒です。

これらのパラメーターは非常に頻繁にアクセスされるため、PHP設計者は厳密な設計原則を守るのではなく、簡単にアクセスできるようにすることを決定しました。最も一般的な操作を便利にすることをお勧めします。そうしないと、プログラマーは毎回同じ長いものを再入力するようにあなたを呪います。


それらへの頻繁なアクセスについては同意しますがIf they were in a class instead of global variables, they would probably be singletonsシングルトンもグローバルスコープにあるため、同意しません。私は単にそのグローバルな状態を完全に削除してローカルにすることについて言っているだけです。クリーンコードトーク-"グローバルな状態とシングルトン"からこのアイデアを借りています。この質問をすることに対する私の主な懸念が表明されています:テスト。残念ながら、質問全体を読まずにタイトルだけを読んだようです
Francisco Presencia

1
私のポイントは、クライアント接続が1つとパラメーターのセットが1つしかないため、これらは本質的にグローバルであるということです。

それはトレードオフです。利便性と設計原則の厳密な順守です。スーパーグローバルを取り除く場合は、$get関数から関数へと渡す必要があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.