3

ユーザーが自分のサイトにログインすると、Userクラスのインスタンスを作成し、ユーザー関連のデータを取得して、オブジェクトをに保存しますSESSION

データベースからフェッチするデータの一部は、セッション全体で一定である必要があり、他のオブジェクトからデータにアクセスできるようにする必要があります。別のオブジェクト内から値を使用User::$static_value_in_classする場合は、を使用する方が好きですが、説得力はあります。$_SESSION['static_value_in_session']

User問題は、インスタンスをにシリアル化してSESSIONから別のページをロードすると、値が記憶されないことです。

クラス定義:

class User {
    public $name;
    public static $allowed_actions;
    public function __construct($username, $password) {
        // Validate credentials, etc.
        self::$allowed_actions = get_allowed_actions_for_this_user($this);
    }   
}
class Blog {
    public static function write($text) {
        if (in_array(USER_MAY_WRITE_BLOG, User::$allowed_actions)) {
            // Write blog entry
        }
    }
}

login.php:

$user = new User($_POST['username'], $_POST['password']);
if (successful_login($user)) {
    $_SESSION['user'] = $user;
    header('Location: index.php');
}

index.php:

if (!isset($_SESSION['user'])) {
    header('Location: login.php');
}
Blog::write("I'm in index.php! Hooray!")
// Won't work, because Blog requires User::$allowed_actions

静的データSerializableの独自のバージョンを実装して書き込む必要がserialize()ありますか?unserialize()

唇を噛んでクラス$_SESSION内から変数にアクセスする必要がありますか?Blog

メソッドUserに送信される有効なインスタンスを要求する必要がありますか?Blog write()

または多分インターネットはより良い考えを持っています...



編集:私の実際のユースケースを書く(完全なコードではありませんが、要点を理解するのに十分です)。

私のサイトは、共有予算アカウントを持つユーザーのグループを処理します。ユーザーは、グループが合意した特定のことにグループのお金を費やすことができ、クラスのインスタンスを作成してデータベースストレージのためTransactionにクラスに送信することにより、トランザクションを報告します。Bank

Bankクラス:

class Bank {
   // Group-agreed reasons to spend money
   public static $valid_transaction_reasons;
   public function __construct(User $user) {
      Bank::$valid_transaction_reasons = load_reasons_for_this_group($user->bank_id);
   }
}

Userクラス:

class User {
   public $bank_id;
   public function __construct($username, $password) {
      $query = "SELECT bank_id FROM users WHERE username=$username AND password=$password";
      $result = mysql_fetch_array(mysql_query($query));
      $this->bank_id = $result['bank_id'];
   }
}

Transactionクラス:

class Transaction {
   public function __construct($reason, $amount) {
      if (!in_array($reason, Bank::$valid_transaction_reasons)) {
         // Error! Users can't spend money on this, the group doesn't cover it
      }
      else {
         // Build a Transaction object
      }
   }
}

実際のコード(login.phpなど):

$user = new User($_GET['uname'], $_GET['pword']);
$_SESSION['bank'] = new Bank($user);

// Some shit happens, user navigates to submit_transaction.php

$trans = new Transaction(REASON_BEER, 5.65);
// Error! Bank::$valid_transaction_reasons is empty!
4

2 に答える 2

6

コメントで述べたように、これはPHPでこれを実現する方法の質問というよりもソフトウェア設計の質問です。

静的プロパティはオブジェクトの状態の一部ではないため、オブジェクトと一緒にシリアル化されません。

関連する問題をどのように解決するかについて、簡単な例を示します。次のメッセージクラスがあり、すべてのインスタンスが一意のIDを持っていることを確認するための静的な$idプロパティがあるとします。

class Message {

    public static $id;

    public $instanceId;

    public $text;

    /**
     *
     */
    public function __construct($text) {
        // the id will incremented in a static var
        if(!self::$id) {
            self::$id = 1;
        } else {
            self::$id++;
        }

        // make a copy at current state
        $this->instanceId = self::$id; 
        $this->text = $text;
    }
}

シリアル化/非シリアル化コード:

$m1 = new Message('foo');
printf('created message id: %s text: %s%s',
    $m1->instanceId,  $m1->text, PHP_EOL);
$m2 = new Message('bar');
printf('created message id: %s text: %s%s',
    $m2->instanceId,  $m2->text, PHP_EOL);

$messages = array($m1, $m2);

$ser1 = serialize($m1);
$ser2 = serialize($m2);

$m1 = unserialize($ser1);
printf('unserialized message id: %s text: %s%s',
    $m1->instanceId,  $m1->text, PHP_EOL);
$m2 = unserialize($ser2);
printf('unserialized message id: %s text: %s%s',
    $m2->instanceId,  $m2->text, PHP_EOL);

IDが複数のスクリプト実行で一意であることを確認するには、さらに作業が必要です。Message::$id最後に実行したスクリプトの値を使用して、オブジェクトを作成する前に、それが初期化されていることを確認する必要があります。これは、Webサーバーでの並列PHP要求に関しては、さらに配線されます。


これは、私が知っている最も単純な静的プロパティであるインスタンスカウンターの単なる例です。この場合、私はそうします。staticただし、副作用なしにプロパティをシリアル化/非シリアル化するには、さらに作業が必要であることがわかります。そして、これはアプリケーションのニーズによって異なります。

この質問には一般的に答えることはできません。静的メンバーをシリアル化することは、どのような場合でも意味がないと言う傾向があります。しかし、私はこれについてのコメントをいただければ幸いです。

于 2013-02-13T17:30:36.440 に答える
1

データベースからフェッチするデータの一部は、セッション全体で一定である必要があり、他のオブジェクトからデータにアクセスできるようにする必要があります。

データが本当に一定である場合は、それらを一定にします。

データが一定でない場合は、それらが個々のユーザー(オブジェクトインスタンス)に属しているのか、一般的な概念(クラスとは何か)としてユーザーに属しているのかを検討してください。

Serializableを実装し、静的データを含めるために独自のバージョンのserialize()とunserialize()を作成する必要がありますか?

静的メンバーは互いに独立しているため、シリアル化されたオブジェクトの文字列に静的メンバーを格納することは意味がありません。それらを保存すると、オブジェクトがシリアル化されたときのクラス状態のスナップショットになります。

次のコードスニペットについて考えてみます。

$user = new User;
$user::$allowed_actions = 'foo';
$string = serialize($user);
unset($user);

ここで、コードの他の部分がこれを行うと想像してください。

echo User::$allowed_actions;

現在メモリにオブジェクトがないにもかかわらず、それでも「foo」が表示されます。それは静的メンバーだからです。クラスの状態です。

今、あなたがこれを行うと想像してください:

User::$allowed_actions = 'bar';

ここでオブジェクトのシリアル化を解除する場合、$ allowed_actionsはどうあるべきですか?フーまたはバー?

$user = unserialize($string);
echo $user::$allowed_actions;

静的メンバーはクラスに関するものであるため、出力は「bar」である必要があります。オブジェクトを作成、破棄、およびそこから戻したという事実は関係ありません。ここで変更したクラスの状態です。

また、静力学は妥当性を損なうものであり、可能な場合はそれらを避けたいことを考慮に入れてください。結局のところ、それはクラス指向プログラミングではなく、OOPと呼ばれています。

唇を噛んで、Blogクラス内から$ _SESSION変数にアクセスする必要がありますか?

いいえ、どこからでもスーパーグローバルにアクセスするべきではありませんが、それぞれのスーパーグローバル、またはその中のデータの抽象化を記述してください。それらは単なる入力ソースです。$_SESSIONやりたいことは、その特定のリクエストに必要なすべてのデータをブートストラップで取得し、代わりにデータを渡すことです。たとえば、ユーザーを再作成して渡します。

Blog write()メソッドに送信される有効なUserインスタンスを要求する必要がありますか?

一般に、メソッドは、アクションを実行するために最も多くの情報を持つオブジェクト上にある必要があります。それがあなたのBlog::writeに当てはまるかどうかはわかりません。allowed_actionsがUserインスタンスの一部である場合、おそらくそうです。有効なUserインスタンスが必要になる可能性があります。

または多分インターネットはより良い考えを持っています...

もう1つのオプションは、アクセス許可を専用のアクセス許可オブジェクトに入れ、ユーザーロールとそのア​​クセス許可を保持することです。次に、Userオブジェクトを渡すことにより、そのリストからアクセス許可を検索できます。可能な実装の詳細については、アクセス制御リスト(ACL)を検索してください。

編集:私の実際のユースケースを書く(完全なコードではありませんが、要点を理解するのに十分です)。

懸念事項が単にBank::$valid_transaction_reasons空である可能性がある場合は、Bankをセッションに保存せず、トランザクションの実行時にユーザーからのみロードします。たとえば、submit_transaction.phpでBankインスタンスを作成します(必要なときに作成します)。 )。そうすれば、エラーが発生することはありません。

于 2013-02-13T17:53:29.547 に答える