<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>The Croton</title>
      <link>http://blogs.grf-design.com/</link>
      <description>Drifting around in Manhattan.</description>
      <language>en</language>
      <copyright>Copyright 2008</copyright>
      <lastBuildDate>Mon, 29 Oct 2007 23:27:48 -0500</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.35</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>SPL: RecursiveIterator</title>
         <description><![CDATA[PHP5 からは、<a href="http://www.php.net/manual/ja/ref.spl.php">Standard PHP Library (SPL) 関数</a> というのが標準でついてきます。標準 PHP ライブラリとかいったら、すぐに使える便利なライブラリなどを想像してしまう僕はおバカさんかな…標準 Interface / Design Pattern 集とでも云ってくれたほうが、僕にはわかりやすいかったかも。実際の所、ほとんどのコードは自分で実装しなければならない。ただ例えば、今回扱うような foreach の挙動をクラスの側から操作できる、というのはなかなか魅力的だし、今までの PHP にはなかった事ですね。

でも使っている人が少ないのか、コードサンプルがあまり見当たりません。今回実装した RecursiveIterator など、一度ハマりどころがわかれば全然簡単なものの、わからないときはさっぱりわからないもの。いわゆる Design Pattern 集に出てくるような内容なので、晒すのもすこし気後れしますが、まぁ何かの足しにはなるでしょう。というわけで以下コード。

<h4>コード</h4>

<em>FooNode.php</em>

<pre>
&lt;?php
<br />
class FooNode implements RecursiveIterator
{
  public $name;
  protected $parent   = null;
  protected $children = array();
  protected $cursor   = 0;
<br />
  public function __construct ($n = null)
  {
    $this-&gt;name = (String)$n;
  }
<br />
  public function __toString ()
  {
    $prefix = null;
    if (!is_null($this-&gt;parent))
    {
      $prefix = (String)$this-&gt;parent . '/';
    }
    return $prefix . $this-&gt;name;
  }
<br />
  public function setParent (FooNode $v)
  {
    $this-&gt;parent = $v;
  }
<br />
  public function add (FooNode $v)
  {
    $this-&gt;children[ $v-&gt;name ] = $v;
    $v-&gt;setParent($this);
    return $v;
  }
<br />
  //Iterator
  public function current ()
  {
    if ($this-&gt;key() !== false)
    {
      return $this-&gt;children[$this-&gt;key()];
    }
    else
    {
      return null;
    }
  }
<br />
  public function key ()
  {
    $keys = array_keys($this-&gt;children);
    $length = count($keys);
    if (($length === 0) || ($length &lt;= $this-&gt;cursor))
    {
      return false;
    }
    return $keys[$this-&gt;cursor];
  }
<br />
  public function next ()
  {
    if (count($this-&gt;children) &gt; 0)
    {
      $this-&gt;cursor = $this-&gt;cursor + 1;
    }
  }
<br />
  public function rewind ()
  {
    $this-&gt;cursor = 0;
  }
<br />
  public function valid ()
  {
    $result = false;
    if ($this-&gt;current() instanceof FooNode)
    {
      $result = true;
    }
    return $result;
  }
<br />
  // RecursiveIterator
  public function getChildren ()
  {
    return $this;
  }
<br />
  public function hasChildren ()
  {
    //trigger_error($this-&gt;name . " hasChildren?");
    return !empty($this-&gt;children);
  }
<br />
  // static function
  public static function traversal (FooNode $f, $d = 0)
  {
    $count = 0;
    foreach ($f as $node)
    {
      //$prefix = str_repeat("", $d);
      //echo $prefix;
      echo $node;
      echo "\n";
<br />
      if ($node-&gt;hasChildren()) 
      {
        $count += FooNode::traversal($node-&gt;getChildren(), ($d + 1));
      }
      ++$count;
    }
    return $count;
  }
<br />
}
</pre>

<em>main.php</em>

<pre>
&lt;?php
<br />
require_once('FooNode.php');
<br />
$a = new FooNode('root');
<br />
$b = $a-&gt;add(new FooNode('hoge'));
  $b-&gt;add(new FooNode(0));
  $b-&gt;add(new FooNode(1));
  $b-&gt;add(new FooNode(2));
<br />
$c = $a-&gt;add(new FooNode('dims'));
$d = $a-&gt;add(new FooNode('dinges'));
<br />
$e = $c-&gt;add(new FooNode(3));
  $e-&gt;add(new FooNode('a'));
  $e-&gt;add(new FooNode('b'));
  $e-&gt;add(new FooNode('c'));
$c-&gt;add(new FooNode(4));
$c-&gt;add(new FooNode(5));
<br />
$f = $d-&gt;add(new FooNode(6));
  $f-&gt;add(new FooNode('d'));
  $f-&gt;add(new FooNode('e'));
  $f-&gt;add(new FooNode('f'));
$d-&gt;add(new FooNode(7));
$d-&gt;add(new FooNode(8));
<br />
$c = FooNode::traversal($a);
<br />
echo "$c times traversed.\n";
</pre>

<h4>使い方</h4>

同じディレクトリに main.php と FooNode.php というファイルを用意して、コマンドラインから <code>php main.php</code> とタイプしてみてください。

<h4>簡単な解説</h4>

FooNode::traversal() というのが、Recursive Iteration をするメソッドです。僕は getChildren と hasChildren が返す値について少々悩みました。hasChildren は Loop の外側から見ると、子供の子供があるかどうかを確認するメソッドですが、この実装では（RecusiveIterator ではなく）Iterator がオブジェクトの子要素を iterate するようになっているため、hasChildren が呼び出されるのは既に Loop が回っている先。だから直近の子供を確認しているのですね。

ただまぁこの traversal メソッド内の if 文で hasChildren なのはちょっとダサい気がするので、外部 iterator を使った実装に変えた方がいいかもなぁ。でも実際には db が絡むからこれでのいいのかもしれない。

あと Iterator::next() や Iterator::current() が 配列関数の <a href="http://www.php.net/manual/ja/function.next.php">next()</a> や <a href="http://www.php.net/manual/ja/function.reset.php">reset()</a> とは少し挙動が違います。僕が前に Iterator の実装をした時は同じようにしていたのですが、今回 <a href="http://www.google.com/codesearch?q=+file:%5C.php%24+RecursiveIterator+show:gEbyQr-K5us:iD-ahTUjWTc:knW3zQNJZYQ&sa=N&cd=9&ct=rc&cs_p=http://www.phpguru.org/downloads/Tree/Tree-2.0.0.tgz&cs_f=Tree-2.0.0/Tree.php#first">http://www.phpguru.org/downloads/Tree/Tree-2.0.0.tgz/Tree-2.0.0/</a> を見つけてこのようにしました。

今よく Manual を見直すと <a href="http://www.php.net/manual/ja/function.RecursiveDirectoryIterator-next.php">void RecursiveDirectoryIterator::next (void)</a> や <a href="http://www.php.net/manual/ja/function.RecursiveDirectoryIterator-rewind.php">void RecursiveDirectoryIterator::rewind(void)</a> とあるので、僕が間違っていたんですね…





また時間を見つけて SPL についても書いてみたいと思っています。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/10/spl_recursiveit.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/10/spl_recursiveit.html</guid>
         <category>php</category>
         <pubDate>Mon, 29 Oct 2007 23:27:48 -0500</pubDate>
      </item>
            <item>
         <title>Status Code を返す / Raw HTTP Request Body を見る</title>
         <description><![CDATA[細かいネタをいくつか。

<h4>API を作る時には Status Code を正しく返す</h4>

外側に公開するときはなおさら、内側で使う時でも、API を作る時に <a href="http://yohei-y.blogspot.com/2007/06/http.html">HTTP ステータスコードを正しく使おう</a> というのは最近の Web 開発のトレンド、なんて云うと怒られるかな。少なくとも、僕は暫く前から気を遣って居ます。（中には行儀の悪い Flash などは 404 を返すと、HTTP Response Body にアクセスできないとかブツブツ…）

ともかく。Symfony を使って、HTTP Response Header 中の Status Code にアクセスするにはどうすれば良いのでしょうか？非常に簡単で、任意のアクションクラスの中で以下のようにします。

<pre>
$this->getResponse()->setStatusCode(400);
</pre>

あれ？404 って何だっけ？という向きに、<a href="http://blogs.grf-design.com/071022/http_status_code.pdf">Status Code チートシート</a> を用意しました。ご自由にお使いください。（尤も <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP/1.1: Status Code Definitions</a> から H3 要素を XPath で切り出して、Word で整形しただけの単純なものですが。）

<h4>連想配列化されていない Raw HTTP Request Body を見る</h4>

PHP で HTTP POST を受け付けると、$_POST という <a href="http://www.php.net/manual/ja/reserved.variables.php">定義済の変数</a> に連想配列として自動的に格納されます。便利ですね。ただ時として、POST や PUT あるいは <a href="http://www.w3.org/TR/WD-http-pep-971121.html#_Toc404743957">その他拡張された HTTP メソッド（動詞）</a> で、連想配列化される以前の生の文字列を見たいときもあるかと思います。これは Symfony に限らず  <a href="http://www.php.net/manual/ja/wrappers.php.php">PHP 入出力ストリーム</a> を使って、以下のようにします。

<pre>
$raw_input = null;
$stream = f o p e n("php://input", "r");
if ($stream) {
  $raw_input = s t r e a m _ g e t _ c o n t e n t s($stream);
  f c l o s e($stream);
}
else
{
  throw new Exception("Input stream open failure.");
}
</pre>

今気がついたけど、『<em>enctype="multipart/form-data" に対しては 使用できません。</em>』ってあるね…という事は、ファイルアップロードを独自処理、とかは出来ないってことか…そんな条件分けをする意味が正直理解しがたいが…まぁしたくないけどね。

以上細かいネタでした。あ〜 MovableType だと f o p e n とか特別な単語を書くと、エラーが出るようになってる…上記変な書き方になってるのはそのせいです。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/10/status_code_raw.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/10/status_code_raw.html</guid>
         <category>symfony</category>
         <pubDate>Mon, 22 Oct 2007 01:55:17 -0500</pubDate>
      </item>
            <item>
         <title>Symfony Tips: Error, Output Escaping</title>
         <description><![CDATA[今日は手短に symfony 設定ネタなどを。

<h4>dev 環境で標準出力への抑制</h4>

前にも『<a href="http://blogs.grf-design.com/archives/2007/09/javascript_symf.html">Symfony で Json を出力</a>』で少し書きましたが、僕は symfony を使って XML やら JSON やら YAML やらをよく生成します。PHP を database の出力用 template にするわけですね。あとはがりがり javascript とか、flash なんかの client side アプリを書いていきます。効率の面から云うと PHP というかサーバ側でもう少し連携というか framework の中から出来る方がいいのかもしれないけれども、まだそこまでは至って居ません。

ともかく。僕みたいに symfony を使って XML やら JSON やら YAML を吐き出させる人にとって、dev 環境が（標準）出力に混ぜる PHP_NOTICE と PHP_WARNING が結構しんどいです。特に XML 文にエラーが混じると、正書式でなくなりプロセスが止まります。出力を止めるには…

<pre>
/apps/APP_NAME/config/settings.yml
</pre>

で

<pre>
dev:
 .settings:
   # E_ALL | E_STRICT = 4095
   # error_reporting:        4095
</pre>

と、error_reporting をコメントアウトします。もちろん、これでも php.ini に設定したエラーログには出ます。

<h4>Output Escaping の設定</h4>

<a href="http://blog.symfony.jp/2007/08/18/symfony-tips-1/">symfonyで開発日記 : symfony tips vol.1</a> で学びました。細かいですが直ぐに役に立つ事ばかりで、非常に興味深いプレゼンです。是非、video と配布資料をご覧になる事をお勧めします。というわけで、その中で僕もこれで知った、Output Escaping のやり方を備忘録がてら、紹介します。

設定は以下のように。

<pre>
/apps/{app}/config/settings.yml
</pre>

<pre>
all:
  .settings:
    escaping_strategy:      both
    escaping_method:        ESC_ENTITIES
</pre>

こうすると、template で出力される変数が全て HTML 実体参照に変換されます。

どうしても変換したくない場合（例えば上述の XML を直接出したい、とか）は２つ方法があります。

<em>String 型の変数の場合</em>

要するに echo で直接出せるものですね。例えば actions.class.php で

<pre>
public function executeFoo ()
{
  $this-&gt;output = "&amp; is ampersand.";
}
</pre>

のようにあると、fooSuccess.php では

<pre>
&lt;p&gt;Escaped: &lt;?php echo $output; ?&gt; &lt;/p&gt;
&lt;p&gt;Original: &lt;?php echo $sf_data-&gt;getRaw('output'); ?&gt; &lt;/p&gt;
</pre>
とします。

<em>Object から method を呼び出す場合</em>

ちょっとわかりにくいですね。僕もあまりしないんですが。例えば actions.class.php で

<pre>
public function executeFoo ()
{
  $this-&gt;obj = new SomeObject();
}
</pre>

のようにあって SomeObject インスタンスは echo() という method で、引数を直接出力できるとします。fooSuccess.php では

<pre>
&lt;p&gt;Escaped: &lt;?php $obj-&gt;echo('&amp; is ampersand.'); ?&gt;&lt;/p&gt;
&lt;p&gt;Original: &lt;?php $obj-&gt;echo('&amp; is ampersand.', ESC_RAW); ?&gt;&lt;/p&gt;
</pre>

という風に、引数の最後に ESC_RAW というおまじないを入れるといいそうです。


ところで、今日は <a href="http://www.symfony-project.com/blog/2007/10/18/two-years-of-symfony">Symfony が公開されて 2年目の誕生日</a>だそうです。<strong>Bonne Anniversaire! お誕生日おめでとう！</strong>]]></description>
         <link>http://blogs.grf-design.com/archives/2007/10/symfony_tips_er.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/10/symfony_tips_er.html</guid>
         <category>symfony</category>
         <pubDate>Thu, 18 Oct 2007 23:10:22 -0500</pubDate>
      </item>
            <item>
         <title>自分で書いた SQL から populated Object を作る</title>
         <description><![CDATA[Symfony の採用する Propel O/R マッパは非常に便利で強力ですが、複雑にネストした query など、効果が発揮できない場面はあります。あるいはそれから生成される SQL の効率が悪いとか。そんなことにいちいち気を遣う僕の頭がおかしいのかもしれないけれど、SQL を直に書いていた頃に出来た事が出来ないのは、Framework を使う理由の本末転倒だなとは感じていました。僕が知らないだけなのかもしれない。ここにある <a href="http://propel.phpdb.org/docs/api/current/runtime/propel.util/Criteria.html">Criteria Object の Reference</a> にある事以上をうまく解説してるサイトとかあったら誰か教えてください。

という訳で、生の SQL Query を Symfony 標準の Propel+Creole を使って使う方法をまとめてみたいと思います。Askeet Tutorial はやや古くなりつつありますが、First Reference として引いておきましょう。

<a href="http://www.symfony-project.com/askeet/1_0/13#Display%20a%20short%20list%20of%20popular%20tags%20for%20a%20question">symfony Advent Calendar: symfony advent calendar day thirteen: Tags</a>

<a href="http://symfony.xrea.jp/askeet/13.html">symfony アドベントカレンダー 13日目: タグ</a>

僕が最初によくわかってなかったのは、O/R マッパがあるのに生の SQL を使うのはなんだか負けた気がする、と云うか、O/R マッパを使えば、もう SQL を使わなくていいのかと思ってた。まともに考えて、SQL を Object に抽象化する、って相当難しそうだし、めんどくさいものだと。でもそれは偏見で、O/R マッパって出来ることは凄い単純で出来ることも限られてる。そのかわり理解しやすい、自動化しやすい、他のソフトウェアから扱いやすい、ということかしらん。

多分肝心なのは、既に SQL を知ってる人は、DB とお話しするのにオブジェクト O/R マッパを使う必要は必ずしも無くて、パファーマンスが必要なときとか、SQL が得意なことをする時は生の SQL を使うべき、だと云うこと。難しくないし、そんなにめんどくさくもない、ということ今回のエントリで解説してみたい。

というわけでやってみましょう。ここでは何か target に tag を付けて、分類したいとします。tag を正規化すると target と tag は m:n な関係になりますね。schema.yml は以下。

<pre>
propel:
  _attributes:      {defaultIdMethod: native, noxsd: true}
<br />
  target:
    _attributes:          { phpName: Target }
    id:
      type:               integer
      required:           true
      primaryKey:         true
      autoIncrement:      true
    content:
      type:               varchar(64)
      description:        "Target content"
<br />
  tag:
    _attributes:          { phpName: Tag }
    id:
      type:               integer
      required:           true
      primaryKey:         true
      autoIncrement:      true
    name:
      type:               varchar(64)
      description:        "Tag name"
<br />
  target_tag_joint:
    _attributes:          { phpName: TargetTagJoint }
    pkey:
      type:               integer
      required:           true
      primaryKey:         true
      autoIncrement:      true
    tag_id:
      type:               integer
      foreignTable:       tag
      foreignReference:   id
    target_id:
      type:               integer
      foreignTable:       target
      foreignReference:   id
</pre>

Fixture として <a href="http://money.cnn.com/magazines/fortune/fortune500/2007/">Fortune 500</a> をネタにしましょう。fixtures/data.yml は最後に付けておきます。実際あんまりいい例ではなかったか…

こんな時に、例えば一つの tag から target object の集合が欲しくて、普通に Criteria を使うと…

<pre>
$tag_name = 'car';
<br />
$c = new Criteria();
$c->add(self::NAME, $tag_name);
$tag_obj = TagPeer::doSelectOne($c);
<br />
$c = new Criteria();
$c->add(self::TAG_ID, $tag_obj->getId());
$arr = self::doSelect($c);
<br />
$result = array();
foreach($arr as $joint)
{
  $c = new Criteria();
  $c->add(TargetPeer::ID, $joint->getTargetId());
  $result[] = TargetPeer::doSelectOne($c);
}
<br />
return $result;
</pre>

こんな感じでしょうか。前述の fixture くらいの小さな集合だったら、全く問題ないレヴェルのクエリですが、もし、例えば、一つの tag から 100 の target が引けるとすると、ちょっとシンドイでしょうね。なにせ 100 回クエリが投げられる訳ですから。

SQL 言語を理解している人には当たり前過ぎかもしれませんが、この問題は以下のような入れ子になった INNER JOIN を使うと、1 回の SQL クエリで同じ集合を得ることが出来ます。（IN 述語を使う方がいいって？後述します）

<pre>
SELECT 
  TAR.id, TAR.content 
FROM 
  target AS TAR 
  INNER JOIN (
    SELECT 
      TTJ.target_id 
    FROM 
      target_tag_joint AS TTJ 
      INNER JOIN ( 
        SELECT 
         TAG.id 
        FROM 
          tag AS TAG 
        WHERE 
          TAG.name = ? 
      ) AS T ON TTJ.tag_id = T.id 
  ) AS RES ON TAR.id = RES.target_id ; 
</pre>

（Join を使った query は禁止とか、アクセスの桁があがると Join は遅いからこういう正規化はダメ、というのは別の話題、ということで。）

このクエリを実行すると、ResultSet オブジェクトになり、例えば配列として結果集合を取得できます。ただ他の部分との整合性として、特別にこの method だけは配列としてアクセス、というのもすこしおかしいし難しい。どうせなら、Criteria を使うのと同じく O/R マッピングをしたい。populate してみます。

<pre>
$con    = Propel::getConnection();
$stmt   = $con->prepareStatement($query);
$stmt->setString(1, $_tag);
$rs     = $stmt->executeQuery(null, ResultSet::FETCHMODE_NUM);
<br />
$result = array();
$cl     = Propel::import(TargetPeer::getOMClass());
<br />
while ($rs->next())
{
  $obj = new $cl();
  $obj->hydrate($rs, 1);
  $result[] = $obj;
}
<br />
return $result;
</pre>

これは、もうまるまま Base***Peer の populateObjects メソッドのコピーです。hydrate の第二引数の int は、Base*** の hydrate のソースを読めばわかりますが、ResultSet の index を 1 から降り直したもの…訳が分かりませんね。ソースを追ってみましょう。以下、symfony propel-build-model で生成された BaseTarget.php から引用します。

<pre>
public function hydrate(ResultSet $rs, $startcol = 1)
{
  try 
  {
    $this->id      = $rs->getInt($startcol + 0);
    $this->content = $rs->getString($startcol + 1);
    $this->resetModified();
    $this->setNew(false);
    return $startcol + 2;
  } 
  catch (Exception $e) 
  {
    throw new PropelException("Error populating Target object", $e);
  }
}
</pre>

propel-build-model した時に、schema.yml をパースして、ResultSet を指数配列で受け取るようにして（ResultSet::FETCHMODE_NUM）順番を降っているだけだったのですね。だからこれをうまく使えば一回のクエリで２つのオブジェクトを受け取る事も可能なわけです。

<h4>まとめ</h4>

Query さえ書ければ、populate の部分はほとんど写経でも動くはずです。大事な点は、Prepared Statement を使って、ちゃんと値を代入する事と、executeQuery(null, ResultSet::FETCHMODE_NUM) のようにして指数配列で ResultSet を受け取る事です。

Hydrate も仕組みを理解すれば便利に使えます。今回の例でいえば、Target のオブジェクトに 1:n な関係の値が入る場合（例えば各 Target 項目に複数毎写真を付けられる、とか）、一つの row に target と picture が一緒にかえるような SQL を書いて、一気に hydrate する、とか。

<h4>捕捉: IN 述語</h4>

今回僕が挙げたクエリの INNER JOIN を IN を使って書き直すと、こんな感じに。

<pre>
SELECT 
  TAR.id, TAR.content 
FROM 
  target AS TAR 
WHERE 
  TAR.id 
  IN ( 
    SELECT 
      TTJ.target_id 
    FROM 
      target_tag_joint AS TTJ 
    WHERE 
      TTJ.tag_id 
      IN (
        SELECT 
          TAG.id 
        FROM 
          tag AS TAG 
        WHERE 
          TAG.name = ? 
      )
  ) ; 
</pre>

先に INNER JOIN を使ったのは…僕がそれに馴れているからで深い意味はないんですが、書いてみて、一緒に走らせてみると、項目が多くなれば INNER JOIN を使ったクエリの方が、メモリは食いますがほんのちょびっと速くなる感じでした。

<a href="http://www.symfony-project.com/snippets/snippet/75">Symfony snippets: Sub-selects using Propel</a> というページに IN 述語を使ったサブクエリを Criteria オブジェクトを使って投げる、という例があります。どっちにしても、Criteria で今回のような 2 段 nest をやる方法はわかりませんし、主題としては Populate Object は難しくないYO! という事なのでよしとします。

続きに fixtures/data.yml を挙げておきます。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/10/_sql_populated.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/10/_sql_populated.html</guid>
         <category>symfony</category>
         <pubDate>Wed, 17 Oct 2007 23:39:28 -0500</pubDate>
      </item>
            <item>
         <title>Preview 用に画像を crop &amp; scale する</title>
         <description><![CDATA[画像を受け取って、それからPreview 用の Thumbnail を生成する、というのは Web app でよくあるシチュエーションだと思います。自分用の備忘録として、今日思いついた方法をメモしておきます。

変換元 / 変換先がどんな画像であれ、crop & scale に必要な情報は

<ul><li>元画像 (source)<ul><li>X 座標</li><li>Y 座標</li><li>幅</li><li>高さ</li></ul></li><li>先画像 (destination)<ul><li>X 座標</li><li>Y 座標</li><li>幅</li><li>高さ</li></ul></li></ul>

この８つです。今回はこの８つのうち、４つの情報、変換元 / 変換先それぞれの幅と高さがわかっている状態から、出来るだけ、aspect 比を変えないようにするアルゴリズムを考えてみました。PHP で手続き風に書きますが、言語（当然ですが）は問いません。

<pre>
function calcResizedBounds ($w0, $h0, $w1, $h1)
{
  switch (true)
  {
    case ($w0 &gt; $h0):
      $r = processLandscape($w0, $h0, $w1, $h1);
    break;<br />
    case ($w0 &lt; $h0):
      $r = processPortrait($w0, $h0, $w1, $h1);
    break;<br />
    case ($w0 == $h0):
      $r  = processSquare($w0, $w1, $h1);
    break;
  }
  return $r;
}
<br />
function processLandscape ($w0, $h0, $w1, $h1)
{
  $src_y = 0;
  $src_h = $h0;
  $ratio = $h0 / $h1;
  $src_w = floor($w1 * $ratio);
  $src_x = floor(($w0 - $src_w) / 2);
  return array($src_x, $src_y, $src_w, $src_h);
}
<br />
function processPortrait ($w0, $h0, $w1, $h1)
{
  $src_x = 0;
  $src_w = $w0;
  $ratio = $w0 / $w1;
  $src_h = floor($h1 * $ratio);
  $src_y = floor(($h0 - $src_w) / 2);
  return array($src_x, $src_y, $src_w, $src_h);
}
<br />
function processSquare ($side, $w1, $h1)
{
  switch (true)
  {
    case ($w1 &gt; $h1):
      $src_x = 0;
      $src_w = $side;
      $ratio = $side / $w1;
      $src_h = floor($h1 * $ratio);
      $src_y = floor(($side - $src_w) / 2);
    break;<br />
    case ($w1 &lt; $h1):
      $src_y = 0;
      $src_h = $side;
      $ratio = $side / $h1;
      $src_w = floor($w1 * $ratio);
      $src_x = floor(($side - $src_w) / 2);
    break;<br />
    case ($w1 == $h1):
      $src_x = 0;
      $src_y = 0;
      $src_w = $side;
      $src_h = $side;
    break;
  }
  return array($src_x, $src_y, $src_w, $src_h);
}
</pre>

こんな風に使います。

<pre>
$dst_x = 0;
$dst_y = 0;
$dst_w = $w1;
$dst_h = $h1;
$src_x = null;
$src_y = null;
$src_w = null;
$src_h = null;
list($src_x, $src_y, $src_w, $src_h) = calcResizedBounds($w0, $h0, $w1, $h1);
</pre>]]></description>
         <link>http://blogs.grf-design.com/archives/2007/09/preview_crop.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/09/preview_crop.html</guid>
         <category>Processing</category>
         <pubDate>Wed, 05 Sep 2007 23:04:24 -0500</pubDate>
      </item>
            <item>
         <title>Symfony で Json を出力</title>
         <description><![CDATA[Symfony にも、Prototype と連携する plugin や、Askeet のサンプルでも出てくる Symfony 同梱の helper などがあります。でも最終的には結局しっくりこないで、自分でみっちりチューニングした javascript を書いてしまうことが僕は多いです。。そんな時に、僕だけかもしれないけれど、Symfony で JSON 形式の書き出しをする事があります。Symfony 側、例えば app.yml に設定を集中して書いて interface を司る javascript でも同じ設定を使い回すため、とか。

で、今日すこしハマったので、tip として挙げておきます。どういう理由なのか、自分ではさっぱりわからなかったのだけれど…

例えば、以下のような Symfony の View があります。（抜粋）

<pre>
var Setting = {};
&lt;?php foreach ($arr as $key =&gt; $val): ?&gt;
Setting.&lt;?php echo $key; ?&gt; = '&lt;?php echo $val; ?&gt;';
&lt;?php endforeach; ?&gt;
</pre>

これを &lt;script src="〜〜"&gt;&lt;/script&gt; として読み込ませてみるんだけど、javascript runtime が解釈してくれない。。ソースを browser で見て、普通にテキストとしてコピペすると、使えるのに…全体が読み込まれないならまだわかるんだけど、一部だけってのも不可解すぎた。

諦めかけた時に、ふと、ページの content-type を text/javascript から、application/x-javascript に変えてみたら、全てのオブジェクトが認識されるようになりました。/apps/modules/MODULE/config/view.yml に以下のように書きました。

<pre>
default:
 has_layout:     off
 http_metas:
   content-type: application/x-javascript
</pre>

white space の扱い、とかそういうのだと思うけれど。こういう事もありますよ、という事で。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/09/javascript_symf.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/09/javascript_symf.html</guid>
         <category>symfony</category>
         <pubDate>Wed, 05 Sep 2007 22:35:13 -0500</pubDate>
      </item>
            <item>
         <title>Singleton Pattern in Javascript</title>
         <description><![CDATA[突然思いついたので、メモ。Google で検索した限り、こういう書き方をしている人はまだ誰もいないはず…どうかしらん。ちなみに meme は même のことで、フランス語で self / same の意味です。

<pre>
var Singleton = function ()
{
 var err  = new Error('This is Singleton pattern. Please use
getInstance() instead');
 err.name = 'SingletonError';
 throw err;
}
<br />
Singleton.meme = Singleton;
<br />
Singleton.initialize = function()
{
 var instance = {
   'code': 'implementation comes here'
 };
 this.meme.getInstance = function (){
   return instance;
 };
 delete this.meme.initialize;
}
<br />
Singleton.getInstance = function ()
{
 this.meme.initialize();
 return this.meme.getInstance();
}
<br />
<br />
try {
 s = new Singleton();
} catch (ex) {
 console.log(ex);
}
<br />
<br />
try {
 s = Singleton.getInstance();
 console.log(s);
} catch (ex) {
 console.log(ex);
}
</pre>

ま〜、結局根が Dynamic で、アクセス制限も微妙な javascript なんで、Singleton パターンがどれほど重要か、という議論はあると思いますが。この考え方を使えば Singleton などを強制できるライブラリとか作れますね。

きっかけは、<a href="http://developer.apple.com/documentation/ScriptingAutomation/Conceptual/JSCodingGuide/Advanced/chapter_5_section_2.html#//apple_ref/doc/uid/TP40006088-CH5-DontLinkElementID_9">JavaScript Coding Guidelines for Mac OS X: Memory and Performance Considerations</a> でした。

<blockquote>Release initialization functions. Code that’s called once and never used again can be deleted after its execution. For instance, deleting a window’s onload handler function releases any memory associated with the function, like this:
<pre>var foo = function()
{
    // code that makes this function work
    delete foo;
}
window.addEventListener('load', foo, false);</pre>
</blockquote>

Web で使ってる限りでは、普通こういう事はしないよなぁ、と思いました。いやもうちょっと正確に言うと、Web で使ってる限り、数分単位以上 走り続ける Javascript なんてあんまり考えないよなぁ。Dashboard は、数日単位で走り続ける可能性があり、そのためにはこれくらいしないと行けないんですね。

これで思い出したのは、イントラネットで使うカレンダーの設計。全てを Javascript で実装しましたが、いちいち Event を delete しないと、オブジェクトへの参照が消えないので、数回の読み込みでメモリがパンクする、という事を発見したことがありました。

オチなしですが、最近 blog ご無沙汰だったので。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/08/singleton_patte.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/08/singleton_patte.html</guid>
         <category>javascript</category>
         <pubDate>Thu, 23 Aug 2007 22:26:31 -0500</pubDate>
      </item>
            <item>
         <title>Windows での logrotate</title>
         <description><![CDATA[Unix には logrotate というログの管理をやってくれる便利なツールがあります。管理というとあれですが、一つの機能として、アプリケーションが取り溜めた古い log ファイルを新しいファイルに切り替えて、ある程度古くなったものは廃棄します。これが revolver を rotate（回転）するような感覚なんでしょうか。

昨日の続きなんですが、Windows 環境で、そのようなユーティリティを探していました。と、そこで、Apache 自身に付属するツールで、rotatelogs.exe というのがあるのを知りました。どれどれどんな使い方…と思って調べてみると…

<a href="http://www.sitebuddy.com/Apache/Rotatelogs.exe/Bug">Apache Rotatelogs.exe for Windows Server</a>

<blockquote>Conclusion: It is unusable and dangerous (it will eat up all your memory/file handlers ...etc...).
You can not even use rotatelogs.exe on 4+ sites, Apache will lockup when starting (tested on Apache 2.2.0).</blockquote>

だそうです。バギーじゃなぁ…で、同じサイトで以下のような記事も見つけました。

<a href="http://www.sitebuddy.com/mod_log_rotate">Apache Better Log Rotation mod_log_rotate</a>

<blockquote>Using the module mod_log_rotate, the log rotation is handled by the server process so you save on the process count and file descriptors. I've compiled that code for windows using VC++ 6 for Apache 2.0.x and Apache 2.2.x. You can find the windows binaries of mod_log_rotate at the bottom of this article.</blockquote>

アイデアとしてはアリですが、どうせ logrotate するなら、php のログもやりたいと思っていたのでちょっともの足りません。

で、暫く後に <a href="http://kamoland.com/comp/apache_win2k.html">Apache+Windows2000小技集</a> というページを見つけて、Windows 付属の Shell script を使って、処理するという方法に落ち着きました。kamo 氏は２つの方法を示していますが、『日付をファイル名に持たせて保存する方式』にすこし改良を加えて使うようにしました。

<pre>
' logArc.vbs
' original idea: http://kamoland.com/comp/apache_win2k.html
<br />
apacheExe   = """C:\Program Files\Apache Software Foundation\Apache2.2\bin\httpd.exe"""
stopApache  = apacheExe & " -w -n ""Apache2"" -k stop"
startApache = apacheExe & " -w -n ""Apache2"" -k start"
<br />
Set logs  = CreateObject("Scripting.Dictionary")
<br />
logs.Add "main-access",   "C:\var\logs\httpd\httpd-access.log"
logs.Add "main-error",    "C:\var\logs\httpd\httpd-error.log"
...
logs.Add "php-error",     "C:\var\logs\php\php-error.log"
<br />
archive = "C:\var\logs\archive\"
today   = Fmt( year(now), 4) & Fmt( month(now), 2) & Fmt( day(now), 2)
<br />
Dim wsh
Dim fso, fo
<br />
Set wsh = WScript.CreateObject("WScript.Shell")
<br />
On Error Resume Next
wsh.Run stopApache, 1, TRUE
<br />
If Err.Number = 0 Then
<br />
  For Each name In logs.Keys
    Set fso = WScript.CreateObject( "Scripting.FileSystemObject" )
    Set fo = fso.GetFile( logs(name) )
    fo.move( archive & today & "_" & name & ".log" )
    If Err.Number <> 0 Then
      'MsgBox Err.Description & "(" & Err.Source & ")"
      Exit For
    End If
    Err.Clear
  Next
<br />
End If
<br />
wsh.Run startApache, 1, TRUE
<br />
Function Fmt( num, digit)
  Fmt = Right( String(digit, "0") & num, digit)
End Function
</pre>

暫く職場で VBScript on ASP をやっていたのが効いているのか、結構すぐ出来ました。VB なんて知らねぇよ、という人のために少し解説すると…

<blockquote><pre>apacheExe   = """C:\Program Files\Apache Software Foundation\Apache2.2\bin\httpd.exe"""
stopApache  = apacheExe & " -w -n ""Apache2"" -k stop"
startApache = apacheExe & " -w -n ""Apache2"" -k start"</pre></blockquote>

ここでは "" (ダブルクォート２つ) や """ (ダブルクォート３つ) が出てきます。僕は Wiki? 下と思いましたが… Java など一般的には、"~~~" のようにダブルクォートで囲まれた間は文字列として処理されます。VB でも同じなんですが、そのダブルクォートでの文字列表現内で、文字としてダブルクォートを使いたい場合どうするか。同じく Java など一般的には \ (バックスラッシュ) を escape 記号として "~~\"~~" のように使う場合が多いんですが、Windows でバックスラッシュには、directory 区切り記号、という大事な役割があり使えません（たぶん）？そこで "" というふうにダブルクォートを２つ使うんですね。最初の３つは、文字列表現の始まりを示すのと、エスケープされたダブルクォートが連なっている、ということですね。

<blockquote><pre>logs.Add "main-access",   "C:\var\logs\httpd\httpd-access.log"
logs.Add "main-error",    "C:\var\logs\httpd\httpd-error.log"
...
logs.Add "php-error",     "C:\var\logs\php\php-error.log"</pre></blockquote>

ここの部分で、一緒に処理するファイル群を指定しています。どんなファイルでも OK 。もちろん、ファイルロックが掛けられているとダメですが…

<blockquote><pre>On Error Resume Next
wsh.Run stopApache, 1, TRUE
<br />
If Err.Number = 0 Then
<br />
  For Each name In logs.Keys
    Set fso = WScript.CreateObject( "Scripting.FileSystemObject" )
    Set fo = fso.GetFile( logs(name) )
    fo.move( archive & today & "_" & name & ".log" )
    If Err.Number <> 0 Then
      'MsgBox Err.Description & "(" & Err.Source & ")"
      Exit For
    End If
    Err.Clear
  Next
<br />
End If</pre></blockquote>

エラー処理を含めた、ファイル処理ですが、<code>On Error Resume Next</code> とすることで、エラーが起きても、プログラムが絶対止まらないようになります。エラーがおきそうな処理のすぐ下で、エラーが起きてないかを確認してから進めないと行けない、という鬼仕様。。<code>If Err.Number = 0 Then</code> はエラーが起きてない時（= (イコールサイン一つ) は普通、代入に使われ、if 節などの比較コンテキストではなんと比較演算子として処理されます。ま、Macromedia Director の Lingo も同じでしたね…）、<code>If Err.Number <> 0 Then</code> はエラーが起こった時に次のブロックが実行されます。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/08/windows_logrota.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/08/windows_logrota.html</guid>
         <category>symfony</category>
         <pubDate>Thu, 23 Aug 2007 21:25:51 -0500</pubDate>
      </item>
            <item>
         <title>Symfony を Apache on Windows で使う</title>
         <description><![CDATA[もう２年も前になってしまうのですが、とあるグループのイントラネットで動くタスクマネジメントソフトを作った事がありました。そこの方々とは、ご縁あってその後もいろいろおつきあいさせてもらっています。この仕事ではマネージャが居てその人がうまくやってくれている、というのが大きいんだと思うんですが。

ともかく。その僕の作ったマネジメントソフトを書き直す作業を暫くやっていました。その当時としてはまだ珍しかった？ Ajax でデータベースからデータを取り出して云々という感じ。まだまだ未熟だった僕は、自作の XML 生成クラスとか作っていたし、見た目にはほとんど気がつかわれなく、Javascript はコピペかつ何をしているのかよくわからない…それはそれは酷いコードを書いていました（一方データベースの設計は悪くなかった、コラム/テーブルの命名はまぁ old fashion だったけれど）。

せっかくなので、全て Symfony を使って書き直してみました。たしか２ヶ月くらいは掛かっていたと思うのだけれど、今回は３週間くらいかな。これなら拡張も容易に出来そうだし、結構満足しています。

ところが最後の最後でつまずいた、というか前に一度やって、つまづくポイントが多かったのをすっかり忘れていた、Windows での Symfony の deploy を今回少しまとめておきたいと思います。

<h4>前提</h4>

<ul>
<li>Apache のインストール</li>
<li>MySQL のインストール</li>
<li>PHP のインストール</li>
</ul>

Apache はそれこそインストーラ一発だし、MySQL は名前付きパイプなど、変な事をしなければ、インストーラが全てやってくれます。PHP だけはファイルレイアウトなどに気を遣うかもしれませんが、まぁそれはまた別の機会にでも。php.exe (CLI) のある directory へ Path が通っていた方が便利ですが、それも必須ではありません。

<h4>手順</h4>

<ol>
<li><strong>pear のインストール</strong><br />Windows では、go-pear.bat という起動すればいいだけ、のはずなんですが、環境にあわせて手直しする必要がありました。こんな感じに。<pre>@ECHO OFF
set PHP_BIN=php.exe
set PHP_INI=C:\etc\php\php.ini
%PHP_BIN% -c %PHP_INI% -d output_buffering=0 PEAR\go-pear.phar
pause</pre>まずこの変更は、インストール完了時に WARNING を出させないためです。なくても大丈夫、のはず（もちろん PEAR のインストールされる先が、include_path に組み込まれていなければ、WARNING はでます）。<br />あと僕は global 環境変数、というのがキライです。バージョンアップをする時、十分な引き継ぎなく誰かのサポートをする時、global 環境変数に何か書いてあったりすると、非常に厄介。なので、PHP も PEAR にも registry は使わせません。確認されますが、断固として Local インストールです。</li>
<li><strong>pear.ini の変更</strong><br />Local 派はここでも少し手を加えます。php.iniの場所をpear.ini 内に書いておかないと、extension などがうまく動きません。<br />pear.ini は PHP の Array を <a href="http://www.php.net/manual/ja/function.serialize.php">serialize</a> したものです。JSON ほど簡単ではないかもしれないけれど、馴れれば手で書けます。大事なのはその array に、php_ini というキーで、php.ini への file path を書き入れる事です。<pre>#PEAR_Config 0.9<br />a:11:{<br />  s:15:"preferred_state";s:6:"stable";<br />  s:7:"php_ini";s:23:"C:\etc\php\php.ini";<br />....<br />}</pre></li>
<li><strong>C:\path\to\pear -c C:\etc\php\pear.ini DO_WHATEVER</strong><br />という感じで、-c オプションで、pear.ini へのパスを渡してやります。そうすることで Local 設定を有効にして、作業できます。あとは unix と同じ、channel-discover / install で完了</li>
</ol>

あとは、Symfony の project を（絶対パス指定）symfony コマンドで作ってから、同じ directory に以下のような symfony.bat を作っておくと、いちいち full path を打たなくてすみます。

<pre>@echo off<br />
SET PHP_COMMAND=C:\php\php.exe
SET PHP_INI=C:\etc\php\php.ini<br />
@setlocal<br />
%PHP_COMMAND% -c %PHP_INI% -d html_errors=off -d open_basedir= -q ".\symfony" %1 %2 %3 %4 %5 %6 %7 %8 %9<br />
if "%OS%"=="Windows_NT" @endlocal</pre>]]></description>
         <link>http://blogs.grf-design.com/archives/2007/08/symfony_apache.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/08/symfony_apache.html</guid>
         <category>symfony</category>
         <pubDate>Wed, 22 Aug 2007 23:56:21 -0500</pubDate>
      </item>
            <item>
         <title>mxmlc/amxmlc のコンパイルの背景</title>
         <description><![CDATA[Flex SDK は

<ol><li>無料 で使えて、</li><li>馴染みのテキストエディタを使ってActionScipt の編集ができて</li><li>.swf か .air がコンパイル出来る</li></ol>

というよいものです。２週間ほど触ってみて理解した事などをまとめてみます。

大事なのは以下の三点。

<ol><li>mxmlc は .as を .swf へ変換する、（java で書かかれた）コンパイラ<ul><li>  その中で、画面への描画をしたいなら、DisplayObject を extends したクラスを作る必要がある。</li><li>    これは予想だけれども、Display Object と全く同じ signature をもつ別のクラスを作れば、Display Object を extends する必要はないと思う。そのかわり Display Object Container やらいろいろ自分で実装せねばならないけれども。でも<a href="http://livedocs.adobe.com/flex/2/docs/images/DisplayObject_subclasses3.jpg">このクラス図が提供する世界観</a>がきらいとか、それではどうにもならない、という人が新しい世界を作れる、というのはすばらしい。</li></ul></li>
<li>amxmlc は、mxmlc に Air 用のプリプロセスを含めたコンパイラ。Air 用のプリプロセス、とは<ul><li>  いくつかの Air 専用メタデータ（アノテーション）を ActionScript 3 の文法内で再現できるようにすること（マクロ展開みたいなもの。でも java のアノテーションも同じように、コンパイル前のプリプロセスでやってるんだよね？）</li><li>  mxml を .as ファイルへ変換する事</li></ul></li>
<li>以上の事より、amxmlc が展開する .mxml という xml ファイルは無名パッケージで同名の public class に変換される、ということ。</li></ol>

例えば、Hoge.mxml というファイルで、以下のような内容だったとする

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt; 
&lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
  layout="absolute" 
  backgroundColor="0xFFCC00"
  &gt;
  &lt;mx:Script source="bootstrap.as"/&gt; 
&lt;/mx:WindowedApplication&gt; </pre>

これは、

<pre>package
{
  import mx.core.WindowedApplication
  public class Hoge extends WindowedApplication
  {
    public var layout:String = "absolute";
    public var backgroundColor:uint = "0xFFCC00";
    /* bootstrap.as の内容がここに挿入される */
    public function Hoge ():void 
    {
    }
  }
}</pre>

という内容の Hoge.as ファイルが作成される事。

<h4>当初の目的</h4>

僕がしたかったのは、mxml を作る人（DreamWeaver なり Flex Builder なり、何らかの Visual Editing Software を使う人。）と、.as のコードを書く人を分けたかった。で、そのために一番簡単なのは、.mxml の root content （上の例で云えば mx:WindowedApplication ）に何らかの event トリガを仕込む事。creationComplete を使うのが一般的かしらん。

でも、それだと、コード側が全権掌握できない。というか、creationComplete の前に何かしたい時どうする、とか、creationComplete で発動させたい関数の名前が変わる時いちいち mxml 作る人に話さなきゃいけないとか、そもそも静的にはアクセスできない関数をトリガしたい、とかいろいろ問題がある。で、どうするのか考えて、僕なりに出した結論が以下のような bootstrap.as で、上の Hoge.mxml から呼び出されている内容です。

<pre>// this code works as a bootstrap
import mx.core.Application;
import mx.events.FlexEvent;
import mx.controls.Alert;<br />
public var myvar:int = 0;<br />
public function initilizer (ev:Event):void
{
  //trace("init: Start"); 
  Alert.show(this);
  trace(this.myvar);
  //trace("init: End");
}<br />
Shell.shell.addEventListener(InvokeEvent.INVOKE, function(ev:InvokeEvent):void {
  // because Application.application is not set yet this moment.
  Application.application.addEventListener(FlexEvent.CREATION_COMPLETE, Application.application.initilizer);
});</pre>

一番大きなポイントは、イヴェントの発動が２段構えなところでしょうか。何故こんな面倒な事をしているかというと、上で一度説明していますが、amxmlc がコンパイルする時、この bootstrap.as は変換された Hoge.as の一部に挿入されるだけ、だからなんですね。もし

<ul><li>  Application.application.addEventListener の行を一番外側に持ってこようとすると動かない→実行時にまだその Object は出来てないから。</li><li>  Application.application.addEventListener でリスンさせている関数が Application.application.initilizer という見慣れない完全修飾型になっているのは、呼び出し元によって、default の名前空間（端的に言えば this ）が変わるから。これは Javascript をやってる人にはなじみが深いと思うのだけれど、instance method 内で this を使うと、普段はちゃんと動くのだけれど、Event からトリガされると this が切り替わってしまって動かないとか。それと同じ。</li><li>  Shell.shell の InvokeEvent.INVOKE には、closure をあてているのは、意味のない関数に名前のあてるが癪だから。</li></ul>

これがわかるまで、グローバルに配置しておきたい変数宣言を何処におくかとか、名前空間とか、めちゃくちゃ悩みました。もう遅いので後で、自分が使うプロトタイプなどをアップしておきます。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/mxmlcamxmlc.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/mxmlcamxmlc.html</guid>
         <category>AIR / ActionScript</category>
         <pubDate>Mon, 30 Jul 2007 23:24:41 -0500</pubDate>
      </item>
            <item>
         <title>LinkedInはじまったな</title>
         <description><![CDATA[なんで元ルームメイト（未だにメールをやり取りしたり、ご飯食べに行ったりしますが、社会的には何の関係もないよ!?）とか、昔好きだった人（かれこれ４年近くメールのやり取りすらないよ？？）とか、People You May Know にでてくんの!? どんな魔法使いですか!?

<a href="mailto:http://www.chikawatanabe.com/blog/2007/07/linkedin.html">『最近LinkedInがまた熱くなってきた』 on On Off and Beyond</a>で、そうらしい事は聞いていたけれど、あまりに衝撃を受けたので、無内容ですがエントリしてみました。

………

よくよく考えると、もしかして名前の検索履歴を使ってんのかな？その履歴だけじゃ膨大な名前がマッチングしそうだけど、それらと地域やら、年代やらをマッチングさせれば不可能でもないかもね。それだったら mixi でもやればできそうだけど、むしろ mixi の方が、その、元カノ／カレな繋がりばかり push されそうで、それはそれは楽しそうかも…]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/linkedin.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/linkedin.html</guid>
         <category>Rant</category>
         <pubDate>Thu, 26 Jul 2007 23:39:58 -0500</pubDate>
      </item>
            <item>
         <title>ActionScript 3 版 print_r</title>
         <description><![CDATA[オールドスクール開発で、print debug はかかせません。そして php で育った僕には、print_r みたいな、データ構造ダンプ関数がかかせません。というわけで作りました。

<h4>使い方</h4>

<code><pre>
import Utils;<br />
trace( Utils.print_r( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) );
</pre></code>

などとすると、

<pre>
[trace] ---------------------------------------------------------------------
[trace] :Array =>
[trace]   [0]:int => 0
[trace]   [1]:int => 1
[trace]   [2]:int => 2
[trace]   [3]:int => 3
[trace]   [4]:int => 4
[trace]   [5]:int => 5
[trace]   [6]:int => 6
[trace]   [7]:int => 7
[trace]   [8]:int => 8
[trace]   [9]:int => 9
[trace] ---------------------------------------------------------------------
</pre>

と、帰ってきます。他に toCamelCase / capitalize ができます。

<h4>既存の問題</h4>

<ul>
<li>コンストラクタ、イヴェントそれにスコープチェインなどは、まずどうやって表示するのがわかりやすいのか、わからない。取得の方法はわかっていて、ソースを見ればだいたいわかると思います。</li>
</ul>

<h4>コード</h4>

<pre>
package
{
  import flash.utils.describeType;<br />
  public class Utils
  {<br />
    public static function print_r (o:*, name:String = "", recur:int = 0):String 
    {
      var result:String = "";
      var type:String   = typeof(o);
      var desc:XML      = describeType(o);<br />
      // meta data of object
      if (recur == 0)
      {
        result += "---------------------------------------------------------------------\n";
      }
      else
      {
        for (var i:int = 0; i &lt; recur; ++i)
        {
          result += "  ";
        }
      }
      if (name)
      {
        result += '[' + name + ']';
      }
      result += ':' + desc.@name + ' =&gt; ';<br />
      // content of object
      switch (type)
      {
        case "boolean":
        case "number":
        case "string":
          result += String(o);
          break;
        case "xml":
            result += o.toXMLString();
            break;0
        case "object":
          if (desc.@name == "Object" || desc.@name == "Array")
          {
            for (var key:String in o)
            {
              result += "\n";
              result += print_r(o[key], key, (recur + 1));
            }
          }
          else
          {
            var prop:XML;
            //trace("UTIL: " + typeof(o));
            //trace("UTIL: " + desc.toXMLString());
            // -- properties
            for each (prop in desc.variable)
            {
              result += "\n";
              result += print_r(o[prop.@name], prop.@name, (recur + 1));
            }
            // -- methods
            for each (prop in desc.method)
            {
              //trace("UTIL: " + prop.toXMLString());
              result += "\n";
              result += print_r(o[prop.@name], prop.@name, (recur + 1));
            }
            // -- constructor
            //&lt;constructor&gt;
            //  &lt;parameter index="1" type="flash.net::URLRequest" optional="true"/&gt;
            //&lt;/constructor&gt;
            // -- inheritance and implements
            //&lt;implementsInterface type="flash.events::IEventDispatcher"/&gt;
            //&lt;extendsClass type="flash.events::EventDispatcher"/&gt;
            //&lt;extendsClass type="Object"/&gt;
            // -- events and others..
            //&lt;metadata name="Event"&gt;
            //  &lt;arg key="name" value="httpStatus"/&gt;
            //  &lt;arg key="type" value="flash.events.HTTPStatusEvent"/&gt;
            //&lt;/metadata&gt;
          }
          break;
        case "function":
          //trace("UTIL: " + typeof(o));
          //trace("UTIL: " + desc.toXMLString());
          result += String(o);
          break;
      }<br />
      if (recur == 0)
      {
        result += "\n---------------------------------------------------------------------";
      }
      return result;
    }<br />
    public static function toCamelCase (str:String):String 
    {
      var r:String = "";
      var a:Array = str.split(' ');
      for (var s:String in a)
      {
        r += Utils.capitalize(a[s]);
      }
      return r;
    }<br />
    public static function capitalize (str:String):String 
    {
      var r:String = "";
      r += (str.slice(0, 1)).toUpperCase();
      r += str.slice(1);
      return r;
    }<br />
  }
}
</pre>]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/actionscript_3.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/actionscript_3.html</guid>
         <category>AIR / ActionScript</category>
         <pubDate>Wed, 25 Jul 2007 02:02:43 -0500</pubDate>
      </item>
            <item>
         <title>Dreamhost に symfony をインストール</title>
         <description><![CDATA[毎日酷く疲れているので箇条書きにて。

<ol>
<li><a href="http://blogs.grf-design.com/archives/2007/04/dreamhost_phpin.html">Dreamhost で php.ini を設定する方法</a> に従って、php.ini を設定。<ul><li>今回のポイントは、pear 用とその他 PHP ライブラリ用とで、別の includes dir を作った事。</li></ul></li>
<li>環境変数（.bash_profile や .cshrc とか）を編集する。<ul><li>デフォルトでは /usr/local/bin が含まれているけれど、使うものがなかったので削除。</li><li>php5 用の pear へ symbolic link を張る。</li><li>
pear の実行ファイルを確認すると、PHP_PEAR_PHP_BIN と PHP_PEAR_INSTALL_DIR という環境変数に意味がありそうな感じがするが、<em>設定するとうまく動かない！</em></li><li>現段階での .bash_profile はこんな感じ <pre>export PATH="$HOME/bin:/usr/bin:/bin"<br />alias mypear="/usr/local/php5/bin/pear"</pre></li></ul></li>
<li><a href="http://blogs.grf-design.com/archives/2006/03/dreamhost_pear.html">Dreamhost で PEAR ローカルコピーを作る</a> を参考に PEAR のローカルコピーを作る。<ul><li>普通の pear ではなく、上で設定した mypear を使う。</li><li>以前と違い、dreamhost は /tmp への書き込みを禁止したようなので、temp_dir と download_dir を設定しないと動かない。</li><li>コマンドをまとめると<pre>mypear config-create /home/USER/php .pearrc<br />mypear config-set temp_dir /home/USER/tmp/pear<br />mypear config-set download_dir /home/USER/tmp/pear<br />mypear install --alldeps PEAR<br />mypear channel-discover pear.symfony-project.com<br />mypear install symfony/symfony</pre></li></ul></li>
<li>PEAR dir 内にある、実行可能ファイルへ path を通す。現段階では mypear はもう使わなくてよくなったので削除。以下サンプル<br><pre>$HOME/php/pear:$HOME/bin:/usr/bin:/bin</pre></li>
</ol>

こんな所でしょうか。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/dreamhost_symfo.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/dreamhost_symfo.html</guid>
         <category>symfony</category>
         <pubDate>Mon, 23 Jul 2007 07:58:36 -0500</pubDate>
      </item>
            <item>
         <title>fdb あるいは Flash Player debug</title>
         <description><![CDATA[最近 ActionScript をすこし真面目にやるようになりました。もうほとんど Java で、まぁ覚えやすいような、みんなそこまで Java 好きなのか… Design Pattern をやったので、それも近いうちにまとめたいです。

さて、今回は小ネタで。Flash では伝統的に trace という標準出力関数があり、皆よく print debug などに使いまくってるはずですが、最新の Flash Player.app でどうやるかを解説します。

僕の記憶では、昔のバージョンの Debug 版 Flash Player.app （昔は Projector とか呼ばれてたような…）では、フローティングパネルが開いて、そこに出ていたりしたような気がします（嘘かも）。もちろん、Flash CS3 の IDE 内ではそのように動作するし、coding から、build、debug まで一貫した環境で出来て便利そうではあります。

僕のやり方は、Flex SDK を使った OLD SKOOL!! 開発なので（emacs まではまだたどり着いてない）、無料で手に入るものでやりましょう。ActionScript 3 なので最新バージョンの Flash Player が必要です。以下のページよりダウンロード。

<a href="http://www.adobe.com/support/flashplayer/downloads.html#fp9">Adobe Flash Player - Downloads</a>

種類がありすぎて confusing ですが、Projector content debugger と書いてあるものを platform 毎に選んでください。ちなみに Flex3 SDK に同梱の Flash Player.app は debug ビルドではありません。（っつぅか何処見ても Debug と Release ビルドの違いがわからないのはどうかと思う。）

さてようやく本題です。

そのまま debug ビルドな Flash Player.app を立ち上げて、trace 文を含む swf ファイルを開こうとすると、debug host は何処だとか聞いてきます。どうやら debug 用に別の server daemon が必要なんですね？ <a href="http://osflash.org/projects">Projects Open Source Flash</a> をみるといくつか興味深いプロジェクトがいくつかあります。試しましたけど、なんというか、ソースコードに手を加えなきゃ行けなかったり、それは納得いきません。

で、どうするか。実は Flex3 SDK には fdb という超本格的な debugger が付属しています。これと Flash Player を連携させると、trace を拾えます。というか連携させないと拾えません。しかも致命的に使いにくい点もいくつかあります。以下使い方を。

<ul>
<li>fdb の中から、Flash Player を起動させる必要があります。</li>
<li>fdb が Flash Player.app を起動するためにのファイルパスを渡してやる必要があります<strong>が</strong>、<strong>fdb は空白を含むパスを理解しません。</strong></li>
<li>ので、まず最初に Debug built Flash Player.app を適当な名前に rename して、空白文字列を含まないパスに copy します。僕は /usr/local/flash/debug.app とかしました。</li>
<li>fdb とタイプすると、interactive shell のようなものが立ち上がります。次に run /usr/local/flash/debug.app などとすると、Flash Player が立ち上がるので、その dock アイコンに swf ファイルをドロップするもよし、メニューからファイルを開くもよし。</li>
<li>通常の terminal shell から、fdb run /usr/local/flash/debug.app などとすると、Debug built Flash Player が立ち上がる所まで行くので、僕はそれを Shell の Alias に登録しました。</li>
<li>そしてその開いた Flash Player で swf ファイルを開くと、fdb が最初に実行を休止します。fdb シェルにて continue あるいは c とタイプすると、実行が再開されます。</li>
<li>fdb シェル上にて help または h とタイプすると、コマンド一覧が表示されます。</li>
<li>一度 swf ファイルを Flash Player から閉じると、fdb と Debug built Flash Player のコネクションは途絶えます。というわけで最初からやり直し！なのです。</li>
<li>また開いたままでしばらくほっとくと、やはりコネクションタイムアウト、とかいって勝手に切られます。</li>
</ul>

こんな所でしょうか。

最近 TextMate に TextEditor を鞍替えしようかと思っています。Skeleton （App-config.xmlとかの）作成ができて、one key stroke でbuild → debug player  の起動まで云ったりしたら便利そう？あぁ、それにしても１行で終わらせるつもりのエントリに１時間もかけてしまった。。]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/fdb_flash_playe.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/fdb_flash_playe.html</guid>
         <category>AIR / ActionScript</category>
         <pubDate>Sat, 21 Jul 2007 00:47:51 -0500</pubDate>
      </item>
            <item>
         <title>routing.yml について: 基本と応用</title>
         <description><![CDATA[最近 Symfony framework で開発するようになりました。まだ使いこなしていない部分も数多くありますが、とても便利です。

中でも今日は <a href="http://www.symfony-project.com/askeet/ja/4">Askeet チュートリアル 4 日目: リファクタリング</a> などで紹介されている routing.yml についてすこし解説しておきたいと思います。

<h4>基本</h4>

routing.yml はアプリケーション内での URL の規約を設定するファイルです。以下のような特徴があります。

<ul>
<li>まず mod_rewrite かまたはそれに準ずるものがホストとなる HTTP Server にインストールされていて、正しく設定されている事が必要です。dev 環境では動いても、prod 環境では動かない、（というか HTTP Server 独自の 404 File Not Found がでる）と言う場合は RewriteBase を設定してうまくいくことが多いようです（Apache の場合）。例えば、httpd.conf の Alias をつかって http://example.com/askeet/ みたいな形で site root でないところを  Symfony の web directory につないだ時には、当たり前かもしれませんが RewriteBase の設定が必要です。</li>
<li>routing.yml 抜きのまっさらな状態では、Symfony の frontend controller は Symfony web root 以下の URL 文字列を /（forward slash）で区切ります。それの奇数番目を key に、偶数番目を value にした array を作って、それを以降のアクションなどに Request Parameters として渡します。<br />つまり、もし http://example.com/askeet に Symfony web directory が map してあって、ユーザが http://example.com/askeet/foo/bar/hoge/huga/123/456 とリクエストしてきた場合は、[ foo => bar, hoge => huga, 123 => 456 ] という風なリクエストが出来るわけです。</li>
<li>デフォルトで、4つ、ルールが書かれています。これらは、したければ自分で変更したってかまいません。そしてこれらには順番には意味があります（順番は後述）。</li>
<li>Symfony の frontend controller は、引数（つまり directory 区切り記号）の数をかぞえているようです。数とパターンが両方一致した、上から数えて一番最初のルールを適用するようです。</li>
<li>パターンにおいて、文字列はそのままの一致。:label のように : (colon) で始めると、"label" を key とし、ユーザの送った内容を value とする array をつくり request parameter に保持されます。* (asterisk) はワイルドカードで、それのあるエントリでは、引数の数は関係なく、* 以降はあってもなくても関係なくなります。また * 以降の値は基本通り、奇数番目が key に、偶数番目が value になる request parameters を作ります。<br/>例えばでフォルトエントリの一つの以下を参考にすると… <pre>default_symfony:
  url:   /symfony/:action/*
  param: { module: default }</pre><ol><li>Symfony web root より最初の引数が symfony という値で、</li><li>第二の奇数をアクションとして</li><li>それ以降の値は奇数/偶数→key/value </li></ol>という風に解釈されます。例えば先ほどの例と同じ設定のサイトに http://example.com/askeet/symfony/admin/foo/bar/123/456 というリクエストを出すと、module: default, action: admin に [ foo => bar, 123 => 456 ] というリクエストパラメータを投げます。</li>
<li>:module と :action だけは特殊なラベルで、他には使えないようです。</li>
</ul>

<h4>応用</h4>

リクエストの時、

<ol>
<li>何も値がなかったら、全ての値を返し、</li>
<li>引数が２つ以上あったら、最初の２つをキーにした値を返し、</li>
<li>それ以外は 404 </li>
</ol>

とかはよくありそうな話です。先ほどの例と同じく、http://example.com/askeet に symfony web directory がmap されているとしましょう。まずは正解から

<pre>part:
 url:    /api/list/:id1/:id2/*
 param:  { module: api, action: list }<br >
error:
 url:    /api/list/:id1/*
 param:  { module: api, action: list }<br >
all:
 url:    /api/list/*
 param:  { module: api, action: list }</pre>

大事なのは api_list_error の項目が api_list よりも後に定義されているという事ですね。あと error と all の両方のエントリの最後が * で終わっている事。そうしないと、例えば http://example.com/askeet で来たのは拾えても、 http://example.com/askeet/ で来たのは拾えません。

そして受け取るアクションでは、以下のようなコードを書くといいかもしれません。

<pre>$req1 = $this-&gt;getRequestParameter('id1');
$req2 = $this-&gt;getRequestParameter('id2');<br />
switch (true) 
{
  case (is_null($req1) &amp;&amp; is_null($req2)):
    $result = "all";
    break;<br />
  case (isset($req1) &amp;&amp; isset($req2)):
    $result = "partial select";
    break;<br />
  default:
    $this-&gt;forward404();
    break;
}</pre>

<h4>まとめ</h4>

routing.yml について解説しました。順番が大事であるという事、* をつける事の有無などの考察は何となくわかっていても、書き出してみる事で自分中でもすっきりまとまりました。

<h4>参考</h4>

<ul>
<li><a href="http://www.symfony-project.com/book/trunk/09-Links-and-the-Routing-System">The Definitive Guide to symfony Chapter 9 - Links And The Routing System</a></li>
<li><a href="http://trac.symfony-project.com/trac/browser/branches/1.0/lib/controller/sfRouting.class.php">branches/1.0/lib/controller/sfRouting.class.php</a><br />しっかり読み込んでないけれど、504 行目から始まる parse という method がここで解説している処理部分の様子。</li>
</ul>]]></description>
         <link>http://blogs.grf-design.com/archives/2007/07/routing_yml-basics-and.html</link>
         <guid>http://blogs.grf-design.com/archives/2007/07/routing_yml-basics-and.html</guid>
         <category>symfony</category>
         <pubDate>Thu, 12 Jul 2007 23:32:40 -0500</pubDate>
      </item>
      
   </channel>
</rss>
