2011年4月19日火曜日

Google Font APIはどうやってマルチブラウザ対応しているのか

WebFontsを読み込む場合のCSSの書き方はブラウザごとに異なるため、こんなsyntaxを使う必要がある。
しかし、Google Font APIで読み込むCSSをではそんな書き方はしていない。
そこで、試しにTangerineのCSSを各ブラウザで読み込んでみて、Google Font APIがどのようにマルチブラウザ対応をしているのか調べてみた。


IE 8

@font-face {
  font-family: 'Tangerine';
  font-style: normal;
  font-weight: normal;
  src: url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RPY6323mHUZFJMgTvxaG2iE');
  src: local('Tangerine'), url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RD8E0i7KZn-EPnyo3HZu7kw') format('woff');
}
URLはIE専用。
2行目のsrcはIE9用?


Firefox 4
@font-face {
  font-family: 'Tangerine';
  font-style: normal;
  font-weight: normal;
  src: local('Tangerine'), url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RD8E0i7KZn-EPnyo3HZu7kw') format('woff');
}
URLはChromeと同じ。


Chrome 10
@media screen {
@font-face {
  font-family: 'Tangerine';
  font-style: normal;
  font-weight: normal;
  src: local('Tangerine'), url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RD8E0i7KZn-EPnyo3HZu7kw') format('woff');
}
}
URLはFirefoxと同じ。
@mediaでscreenだけにしているのはAndroid対策か何かか?


Safari 5 (for Windows)
@font-face {
  font-family: 'Tangerine';
  font-style: normal;
  font-weight: normal;
  src: local('Tangerine'), url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RKCWcynf_cDxXwCLxiixG1c') format('truetype');
}
URLはSafari用(Operaと共通)。アンダーバーで区切ってるのはiPhone用(iOS用?)が別にあったりするためか?


Opera 11

@font-face {
  font-family: 'Tangerine';
  font-style: normal;
  font-weight: normal;
  src: local('Tangerine'), url('http://themes.googleusercontent.com/font?kit=HGfsyCL5WASpHOFnouG-RKCWcynf_cDxXwCLxiixG1c') format('truetype');
}
URLはSafariと同じ。OperaもTrueType組。


まとめ
おそらくUser-Agentで判断して振り分けているだけのようだ。
フォントファイルは大きく分けると下記の3つに分かれるようだ。
  • IE8-
  • IE9+、Firefox、Chrome
  • Safari、Opera


参考:Webフォント (Webfonts)を使う際の最新記述方法 Fontspring Syntax (The New Bulletproof@Font-Face Syntax) - フォントブログ

2011年4月11日月曜日

PHPのエラーメッセージのマニュアルへのリンクを変更する

PHPのfunctionでエラーが発生すると、エラーメッセージが該当するfunctionへのリンクになる場合がある。

array_merge('foo');

Warning: array_merge() [function.array-merge]: Argument #1 is not an array in /var/www/html/test.php on line 2

このリンクのリンク先は、デフォルトでは有効なURLではない。

php.ini等でマニュアルの場所を指定してあげると、マニュアルの該当functionへのリンクにすることができる。
ini_set('docref_root', 'http://jp.php.net/manual/ja/');
array_merge('foo');

Warning: array_merge() [function.array-merge]: Argument #1 is not an array in /var/www/html/test.php on line 3

このエラーメッセージをブラウザで見る場合は便利な場合もあるかもしれないが、このエラーメッセージはそのままエラーログにも出力されるので、エラーログが余計に増えてしまう。
(エラーログ上のマニュアルのURLを使うなら便利かもしれないが。)
このリンクを抑制するにはhtml_errorsをOffにする。
ini_set('html_errors', false);
array_merge('foo');

Warning: array_merge(): Argument #1 is not an array in /var/www/html/test.php on line 3

すっきりしたね。


参考:PHP: 実行時設定 - Manual

2011年4月5日火曜日

PostgreSQLで日付や日時の差を計算する

PostgreSQLでは単純に引き算すれば日付間の差を取得できる。

SELECT '2011-12-31 23:59:59' - TIMESTAMP '2011-01-01 00:00:00' -- => "364 days 23:59:59"
結果はInterval型になる。

もちろん普通の日付型や日時型の列でもOK。
SELECT updated_at - created_at



条件として使う

条件として判定したい場合は下記のように文字列と比較できる。
SELECT '2011-12-31 23:59:59' - TIMESTAMP '2011-01-01 00:00:00' > '300 days' -- => TRUE
SELECT '2011-01-31 23:59:59' - TIMESTAMP '2011-01-01 00:00:00' > '300 days' -- => FALSE
SELECT '2012-12-31 23:59:59' - TIMESTAMP '2011-01-01 00:00:00' > '1 year' -- => TRUE
左辺を考慮して'300 days'のような文字列はInterval型にCASTして比較してくれる。
もちろWHERE句でも使える。



注意点

DATE型とDATE型の引き算の場合は結果がInterval型ではなく、日数を表すInteger型になる。
SELECT '2011-12-31' - DATE '2011-01-01' -- => 364
これを条件として判定したい場合は、'300 days'のような文字列(あるいはInterval型)ではなく、素直に数値で判定するか、あるいはTIMESTAMP型にCASTにする。
-- 数値で判定
SELECT '2011-12-31' - DATE '2011-01-01' > 300

-- TIMESTAMP型にCAST
SELECT '2011-12-31' - TIMESTAMP '2011-01-01' > '300 days'



参考:日付/時刻関数と演算子 (PostgreSQLマニュアル)

2011年4月4日月曜日

PHPの crypt()で SHA2のハッシュを生成する

PHPのcrypt()を使うと、パスワードで使うのに便利な文字列を生成できる。(説明のために3色に色付けした。)

$str = crypt('test'); => $1$X0zFikbH$z9QaylK29Eu4VQajK6PKw0

生成された文字列は$記号で区切られた3つの部分から構成されている。
  • $1$ → MD5でハッシュを生成したことを表す
  • X0zFikbH → ハッシュ生成に使ったsalt。crypt()の第2引数でsaltを指定した場合は指定したsaltが使われるが、指定しないと自動生成されたsaltが使われる
  • z9QaylK29Eu4VQajK6PKw0 → 生成されたハッシュ


この文字列を使ってパスワードが正しいか確認したい場合、下記のようにcrypt()だけで確認できる。
if (crypt($input_password, $str) === $str) {
    echo 'OK';
} else {
    echo 'NG';
}

crypt()の第2引数としてcrypt()で生成した文字列を渡すと、saltの部分だけを取り出してsaltとして使ってくれる(後ろのハッシュ値は無視される)ので、わざわざsaltだけを取り出す必要はない。(便利だが一見しただけでは分かり辛い...)


crypt()の第2引数でsaltを指定しなかった場合のハッシュアルゴリズムは、手元の環境ではMD5が使われた。
第2引数でsaltを指定する場合は、saltの前に付ける文字列によって下記のようにハッシュアルゴリズムを指定することができる。
  • 何も付けない → DES(拡張DESではなく標準DES?)
  • $1$ → MD5
  • $2a$ → Blowfish
  • $5$ → SHA-256 (PHP 5.3.2以降)
  • $6$ → SHA-512 (PHP 5.3.2以降)

上記のように、PHP 5.3.2以降ではcrypt()でSHA2(SHA-256/512)が使えるようになった。
(既に脆弱性が発見されているSHA-1はスキップされたようだ。)

BlowfishやSHA-256/512を使う場合は自動生成のsaltが使えないので、文字列生成時にはsaltを渡してあげないといけない。
saltは推測可能な値でも構わない(元の文字列が同じでもハッシュが異なるようにすることに意味がある)ので、uniqid()あたりでいいんじゃないかな。
$str = crypt('test', '$6$' . uniqid());


参考:PHP: crypt - Manual


関連記事

2011年4月1日金曜日

PHPで 簡単にGET / POSTできるように file_get_contents()を拡張する

file_get_contents()は便利だが、POSTとかエラー対応とかは少し面倒なので、手軽に使えるようにラップしてみた。
PHPのバージョンは5.2.10以降または5.3.0以降が対象。

function file_http(
    $url,
    $params = null,    //パラメータがあれば配列で渡す
    $method = 'GET',    //GET or POST
    &$status = null,    //参照渡しでレスポンスのHTTP Status Codeを返す
    &$http_response_header = null,    //参照渡しでレスポンスのHTTP Headerの配列を返す
    $request_header = array()    //追加したいリクエストのHTTP Headerがあれば
) {
  
    $opt = array(
        'method' => $method,
        'header' => (array)$request_header,    //PHP 5.2.10 / 5.3.0 以降は配列でもOK
        'ignore_errors' => true,    //PHP 5.2.10 / 5.3.0 以降で有効
    );
  
    if ($params) {
        $query_string = http_build_query($params);
      
        if ($method === 'GET') {
            $url .= '?' . $query_string;
        } else {    //POSTの場合
            $opt['header'][] = 'Content-type: application/x-www-form-urlencoded';
            $opt['content'] = $query_string;
        }
    }
  
    $res = file_get_contents($url, false, stream_context_create(array('http' => $opt)));
  
    //レスポンスのHTTP Headerは$http_response_headerにセットされている
    if (is_array($http_response_header)) {
        preg_match('@^HTTP/1\\.. ([0-9]{3}) @i', $http_response_header[0], $matches);
        $status = $matches[1];
    }
  
    return $res;
}

使用例
$res = file_http(
    'http://localhost/test.php',
    array('foo' => 'テスト'),
    'POST',
    $status,
    $responseHeaders,
    array('User-Agent: ゆーざーえーじぇんと', 'Referer: りふぁら')    // 1つだけの場合は文字列でもOK
);

echo $status;    // => "200"
var_dump($responseHeaders);

実際に使う場合には結果の判断等でレスポンスのHTTPステータスコードが欲しい場合があるので、パラメータの参照渡しで取得できるようにした。
そしてステータスコード以外のHTTP Headerを参照したいケースもあるので、これも参照渡しで配列として取得できるようにした。

また、ignore_errorsを指定することにより、レスポンスのHTTPステータスコードが400以上でもWarningを出さない、かつレスポンスのBodyを取得できるようにした。(参考:[メモ] PHPのfile_get_contentsを、HTTPリクエストに使うときのTIPS ::ハブろぐ
ただしホスト名の名前解決ができない場合はその手前でWarningが出てしまうので、PHPで URLのホスト名(ドメイン)が存在するかチェックする方法のような対応が必要。


以下は余談。
普段は変数名等はcamelCaseにするのだが、$http_response_header等に引っ張られてアンダーバー繋ぎにしてみた。
関数名は、
  • http_request
  • http_get
  • httpRequest
などを考えたのだが、どれもPECLで既に使われているので諦めた。

ブログ アーカイブ

tags