PHPer Tips

PHPer Tips

日本語の情報が少ないPHPフレームワークSymfony2を中心とした話題を集めています。

Amebaでブログを始めよう!
CSRF token is invalid.

Symfony2で開発を進めていて、formオブジェクトにあるisValidを使って($form->isValid())、上記のエラーに遭遇したことありませんか?

このブログの読者で、さすがにCSRFってなに? って人はそんなにいないと思いますが、(いたら、とりあえずぐぐってみてください)
CSRF tokenなんて埋めてないよって、戸惑う人は結構いるかもしれません。

Symfony2では、備え付けのform機能($form->createViewなど)を使うと、自動的にCSRF対策をしてくれます。

frontに送られているformオブジェクトにtokenが含まれているわけです。

twigで、{{form_widget(form)}} のように全要素を一度に呼び出した場合は、良いのですが、問題は、
以下のように個別に表示させた場合です。
{{form.name}}
{{form.address}}

UIの都合上、上記のようにしたい場合も多いでしょう。
しかし、この場合、formオブジェクトに埋め込まれているCSRF tokenがPostされません。
そこで、Model上にて、$form->isValid() を実行すると、falseとなってしまいます。
$form->getErrors() を見ると、表題のエラーが出ていると思います。

これを改善するには、

{{form._token}}をtwigに埋め込んでやる必要があります。

あるいは、{{form_rest(form)}}でもOKです。
ただし、この場合、form_widgetで表示していない残りすべての項目がレンダリングされますのでご注意。

また、Ajaxなどを使用してPostしたものを受け取って $form->isValid()する場合に、備え付けのCSRF機能ではなく、独自のキーを渡すこともあると思いますが、その場合は、form_typeのオプションで以下のように機能を停止する必要があります。

public function getDefaultOptions(array $options)
{
return array(
'csrf_protection' => false,
);
}


[よくあるfor文の落とし穴]

慣れてくると、どんどん省略したり、ワンライナー(一行)で書けるものは、
そうしたいと考えると思います。
それによって、ソースコードが簡潔になるのは良いことです。

しかし、それにも落とし穴があります。
たとえば以下のようなコードを書いていたりしませんか?


for ($i = 0;$i < count($lists); $i++)
{

//処理


}


$i < count($lists) すると、よりまとまったように感じるかもしれませんが、
実はこれは非常に問題があります。

というのも、for文のループの回数分、count($lists)が実行されてしまうからです。

このループが1000回であれば、1000回count($lists)が実行されることになります。

面倒でも、


$counts = count($lists);
for ($i = 0;$i < $counts; $i++ )
{


}


このようにするべきでしょう。
ちなみに実際に以下のコードでどれくらい違うか計ってみましょう。


//とりあえず要素が10000個ある配列作る。
$n = 10000;
while($n--) {

$list[] = "test";

}

//中に入れる場合。
$start = microtime(true);
for ($i=0;$i <=count($list);$i++){}
$end = microtime(true);

echo $end - $start;

echo "\n";

//外に出す場合。
$start = microtime(true);

$counts = count($list);
for ($i=0;$i <=$counts;$i++){}
$end = microtime(true);

echo $end - $start;




結果は歴然
0.0017509460449219
0.0004489421844482