parent::setName($value)は使わずに$this->_set('name', $value)を使え

昨日の続き。
まず、現象の再現方法。
現在某ブログでaskeetをsymfony1.2で実践中なのですが、
symfony+Doctrineでレコード(Doctrine_Record)のプロパティ(body)のセッター部分をハックし、html_bodyというプロバティにMarkdown形式で上書きたい場合のお話です。
本家チュートリアルのように、意識せずPropelのコードと同様に以下のようなaskeetのコードを書いてブラウザから登録を行うと。。

class Question extends BaseQuestion
{
  ....
  public function setBody($v)
  {
    require_once('markdown.php');
    parent::setBody($v); // <= ここで落ちる
    // strip all HTML tags
    $v = htmlentities($v, ENT_QUOTES, sfConfig::get('sf_charset'));
    $this->setHtmlBody(markdown($v));
  }

見事にセグメンテーションエラー。(PHP5.2.6)

[notice] child pid 23191 exit signal Segmentation fault (11)

しかも、不思議なことにdoctrine:data-loadの場合はエラーで落ちずにちゃんと読み込んでくれます。

parent::setBodyメソッド部分で落ちているのはわかったのですが、原因がわからない。。
確かにDoctrineはマジックメソッドを多用しているので、Propelのように親クラスにはsetBodyというメソッドはありません。
でもそれで動くのがマジックメソッドのはずなのに。。。

で、公式ドキュメントみても該当するものが無いと諦めかけていたときにようやく見つけました。まさにこの現象についての記事を。

The _set() and _get() methods must be used to avoid a infinite loop when overriding accessors and mutators.

アクセッサを上書きすると無限ループになっちゃうと。なので、アクセッサとしてDoctrine_Recordに用意された_set()メソッドと_get()メソッドを使えと。
というかまぢですか。

とりあえず、以下のコードがプロパティを上書きするための正しいDoctrineのコード。

class Question extends BaseQuestion
{
  ....
  public function setBody($v)
  {
    require_once('markdown.php');
    $this->_set('body', $v);
    // strip all HTML tags
    $v = htmlentities($v, ENT_QUOTES, sfConfig::get('sf_charset'));
    $this->_set('html_body', markdown($v));
  }

これは知らないとはまる。