2009年11月28日土曜日

Googleサイト内検索結果でページの下端が切れる問題

検索向けAdSenseによるサイト内検索で、IE以外だと検索結果の下端が切れてしまう問題について。


【発生条件】
検索結果を自分のサイト内に表示する場合に起きる。
広告を右のみにした場合に起きた。広告を他の位置にしたら起きないかも。
Firefox(3.5)、Google Chrome(4)、Safari(4 Windows版)で発生を確認した。


【現象】
検索結果の内容に関わらず、検索結果を表示するiframe(JavaScript http://www.google.com/afsonline/show_afs_search.js により生成される)の高さが常に1200pxになる。
その結果、検索結果が少ない場合は検索結果とフッターの間に大きな空白ができる。反対に検索結果が多い場合、検索結果の下端に表示されるページ番号の表示が途中で切れてしまったり表示されなくなってしまう。(実際にはframeの外側にあり、かつframeのスクロールバーが非表示になっているので隠れているだけだが。)


IEの場合、iframe内の検索結果のHTMLのbody要素に、onloadで下記のようなJavaScriptを実行している。

onload="window.top.location='http://(親windowの検索結果のURL)#'+document.body.scrollHeight;"
これにより、親windowのURLの末尾にiframe内のコンテンツの高さを表すhashが付く。

そして検索結果のshow_afs_search.jsの方でlocation.hashを取得し、それをiframeの高さとして設定している。(0.01秒ごとに実行するという監視をしている。高さが取得できた後も。)
その結果、検索結果のコンテンツの内容に応じて検索結果をリサイズするというユーザーフレンドリーなユーザーインターフェイスを実現している。

試しに検索結果を表示した後、URLの最後に「#500」などと付けると検索結果の高さが変わる。これはFirefoxでも有効。


なぜIE以外ではiframe内のbodyのonloadのJavaScriptを付けないのだろう?Firefoxのセキュリティ等により無意味なんだろうか?


【回避策】
検索結果を表示するページのgoogleSearchXxxを設定しているJavaScriptに、下記のように高さを明示的に指定してあげる。
var var googleSearchFrameWidth = 800; //これは既存のコード
var googleSearchFrameHeight = 1210; //これを追加する
これにより、デフォルトの高さが1210pxになり、検索結果の下端が切れなくなる。

IEの場合、これでもリサイズが有効なので、この値は無視してリサイズしてくれると思う。

CakePHPの GetText (i18n)で複数形の翻訳をする

はまったのでメモ。最初からリファレンスや参考サイトを見ながらやれば書いてあったんだろうけど。


CakePHPで単数と複数で異なる翻訳を付けようとした。

__n(' person', ' people', $count);
poファイルも書いた。
msgid " person"
msgid_plural " people"
msgstr[0] "人"
msgstr[1] "人"

するとエラー(Warning)が表示された。
Warning (2): strlen() expects parameter 1 to be string, array given ...


該当箇所はCORE/cake/libs/i18n.phpのtranslate()。
if (is_array($trans)) {
if (isset($trans[$plurals])) {
$trans = $trans[$plurals];
}
}
if (strlen($trans)) {
$singular = $trans;
return $singular;
}
strlen()に配列を渡しているようだ。
でもその上で$transが配列の場合は文字列に入れ替えてるはず。(変数の使いまわしは良くないと思う。)それが効いてないのは「isset($trans[$plurals])」がfalseになっているということか。

この$pluralsをセットしているところを追うと、今回の場合は同じファイル内の__pluralGuess()によりセットしているようだ。
__pluralGuess()の中を追うと、$headerの中身で判断してるようだ。
if (strpos($header, "plurals=3")) {
if (strpos($header, "100!=11")) {
if (strpos($header, "10<=4")) {
return $n % 10 === 1 && $n % 100 !== 11 ? 0 : (略)

$headerを出力してみる。
nplurals=INTEGER;plural=EXPRESSION;

ん?さっきのif文の条件と似ても似つかない。
これは...poファイルで何か設定不足?

poファイルの最初の方を見てみる。
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
まさにこれだ。

こうしてみる。(キャッシュの削除も忘れずに。)
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

動いた!

以上。

2009年11月6日金曜日

PHPで Fatal Error時の状況を調べる方法

PHPでFatal Errorが発生すると以降の処理が実行されない。
かつ、try/catchやset_error_handler()ではFatal Errorを捕まえられない。
なので通常は、Fatal Errorが起きたときの詳しい状況はログに残らない。
(例えばエラーが起きたPHPのファイル名はログに残るが、それを呼び出した側のPHP名や、データの状況は分からない。)

しかし、register_shutdown_function()を使えばエラーが起きたときの状況をログに残せる。

register_shutdown_function('shutdownHandler');

function shutdownHandler(){
$error = error_get_last();
if ($error['type']) { //エラーの場合のみ
error_log(...); //必要な情報を書き出す。
}
}

ただしこれだとE_NOTICEとかでもひっかかってしまうので、Fatal Errorだけを拾いたい場合はtypeを指定する。
register_shutdown_function('shutdownHandler');

function shutdownHandler(){
$error = error_get_last();
if ($error['type'] == E_ERROR) {
error_log(...); //必要な情報を書き出す。
}
}
これだとExceptionが拾えない?


参考:
PHP の「エラー処理ハンドラ」「シャットダウンハンドラ」「例外処理ハンドラ」の挙動 - Web/DB プログラミング徹底解説
PHP: register_shutdown_function - Manual

JavaScriptで画像の大きさを縮小する方法

半分にするならこんな感じ。

<img src="test.jpg" alt="test" onload="this.width /= 2" />


最大値を決めてそれ以下になるようにするならこんな感じ。
<img src="test.jpg" alt="test" onload="this.width = Math.min(this.width, 100)" />


widthを変えれば勝手にheightも同じ比率で変わってくれるようだ。
Firefox3.5とIE7のみで確認。

CakePHPで用意されている layout

CakePHPでは、コントローラでlayoutを指定すれば任意のlayoutを使うことができる。
通常layoutは自分で用意するが(たいていはdefalut.ctp)、いくつか組み込みのlayoutが用意されている。
それぞれどんな出力になるか試してみた。

なお、以下のサンプルで使っているviewの中身は下記の通り。

test


1. $this->layout = 'ajax' の場合の出力結果
test
<!-- 1.7983s -->


2. $this->layout = 'flash' の場合の出力結果
(コントローラ内で$this->flash('foo', 'bar');をセット済み)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>foo</title>

<style><!--
P { text-align:center; font:bold 1.1em sans-serif }
A { color:#444; text-decoration:none }
A:HOVER { text-decoration: underline; color:#44E }
--></style>
</head>
<body>
<p><a href="/test/bar">foo</a></p>
</body>
</html><!-- 1.6536s -->
詳細はよく分からないが、何かFlashのために便利なものなのだろう、きっと。


その他にjs、xml、rssというディレクトリが用意されているがそのままでは使えない。マニュアルを参考に自分でlayoutを作るようだ。(RSSの場合はこちら(Layout :: Controller Code :: Creating an RSS feed with the RssHelper :: RSS :: Core Helpers :: The Manual :: 1.2 Collection :: The Cookbook)をコピーすればすむかも。


参考:Layouts :: Views :: Developing with CakePHP :: The Manual :: 1.2 Collection :: The Cookbook

ブログ アーカイブ

tags