symfonyでsfUserが$_SESSIONに書き込みをするタイミング

第46回PHP勉強会@関東を開催します。

第46回PHP勉強会@関東 - events.php.gr.jp

PHP勉強会募集開始しましたが、ほぼ間違いなく炎上してるのでいけません。残念。
以下本題。

                                                            • -

通常symfonyでセッションに値を入れようと思った場合、

<?php
$user = sfContext::getInstance()->getUser();
// アクションなら$this->getUser()

$user->setAttribute('key', $value, $namespace = 'myapp/namespace');

上記のようにsfUserオブジェクトに対してsetAttributeメソッドを実行し値を入れます。
ですが、このタイミングでは$_SESSIONには書き込まれていません。setAttributeを実行したタイミングでは、sfUserオブジェクトの内部で値を保持しているだけです。

実際に書き込みを行うタイミングはスクリプトが終了するタイミングになります。どういうことかというと、まあソースを見た方がはやいかな。

<?php

class sfUser
{
  public function __construct(sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
  {
    $this->initialize($dispatcher, $storage, $options);

    if ($this->options['auto_shutdown'])
    {
      register_shutdown_function(array($this, 'shutdown'));
    }
  }

  public function initialize(sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
  {
    // ...

    $this->attributeHolder = new sfNamespacedParameterHolder(self::ATTRIBUTE_NAMESPACE);

    // read attributes from storage
    $attributes = $storage->read(self::ATTRIBUTE_NAMESPACE);
    if (is_array($attributes))
    {
      foreach ($attributes as $namespace => $values)
      {
        $this->attributeHolder->add($values, $namespace);
      }
    }

    $currentCulture = $storage->read(self::CULTURE_NAMESPACE);
    $this->setCulture(!is_null($this->options['culture']) ? $this->options['culture']
      : (!is_null($currentCulture) ? $currentCulture : $this->options['default_culture']));

    // ...
  }

  public function shutdown()
  {
    // remove flash that are tagged to be removed
    // ...

    $attributes = array();
    foreach ($this->attributeHolder->getNamespaces() as $namespace)
    {
      $attributes[$namespace] = $this->attributeHolder->getAll($namespace);
    }

    // write attributes to the storage
    $this->storage->write(self::ATTRIBUTE_NAMESPACE, $attributes);

    // write culture to the storage
    $this->storage->write(self::CULTURE_NAMESPACE, $this->culture);

  }

}

register_shutdown_function — シャットダウン時に実行する関数を登録する

PHP: register_shutdown_function - Manual

コンストラクタにてregister_shutdown_function関数でsfUserオブジェクトのshutdownメソッドを登録しています。
そしてshutdownメソッドでattributeHolderの中身を1つの配列にまとめ、$this->storageに書き込む = $_SESSIONに書き込むということを行っています。

ちなみにコンストラクタの第2引数にsfStorageを受け取っています。デフォルトではここにsfSessionStorageオブジェクトが渡され、このsfSessionStorageクラスが$_SESSIONと同等の役割を持つクラスになります。

<?php
class sfSessionStorage extends sfStorage
{
  public function read($key)
  {
    $retval = null;

    if (isset($_SESSION[$key]))
    {
      $retval = $_SESSION[$key];
    }

    return $retval;
  }

  public function write($key, $data)
  {
    $_SESSION[$key] = $data;
  }
}

これ以外にも、sfCacheSessionStorageやsfMysqlSessionStorageなど様々な方法でセッション管理を行えますが、どのsfStorageを使用していようと、基本的にはsfUserが生成されたタイミングで読み込まれ、スクリプトが終了するタイミングで書き込まれます。

通常アプリケーションを構築するときにこの仕組みを意識する必要はありませんが、なんか値が入ってないけどどうなってんのかなーとか思ってアクションとかで$_SESSIONを直接見ても、まだそのタイミングでは変わってませんよ、ということです。