目指せ1級!30代サラリーマンボルダリング日記

都内IT企業で働く30代サラリーマンのクライミング記録

カテゴリ: web

Web上で変換サービスはたくさんあるんだけれど、サーバ内で変換して出力するパターンはあまり検索にも引っかからなかったのでメモ。

OSはCentOS6。
OpenOfficeとJODConverterをインストール。
このあたりの記事参照。

OpenOffice.orgをドキュメント変換ツールとして活用する方法
JODConverter
OpenOfficeを使ってPDFを作成

# export DISPLAY=:1.0
# Xvfb :1 &
# Xvfb :1 -screen 0 1024x768x8
# soffice -headless -accept="socket,port=8100;urp;" -nofirststartwizard &
# netstat -anp | grep 8100
# java -jar /path/to/jodconverter-2.2.2/lib/jodconverter-cli-2.2.2.jar /tmp/sample.docx /tmp/sample.pdf


    このエントリーをはてなブックマークに追加

ということで付けてみましたOGP。

今までこんな感じでイイネ!だったのが、


こうなりました。


groupon-now.jpがグルーポンなうになっただけだけどね。。
最近各クーポンサイトがOGP導入し始めてて、クロールする側としては便利でうれしい。


設定したときにちょっとハマったのでメモ。
og:typeをwebsiteにするのはトップページだけぽい。
なので個別の商品ページはarticleで設定したらうまくいった。

Open Graph protocol - Facebook開発者によると

Use article for any URL that represents transient content - such as a news article, blog post, photo, video, etc. Do not use website for this purpose. website and blog are designed to represent an entire site, an og:type tag with types website or blog should usually only appear on the root of a domain.


てことなので、グルーポンなうでいうと
トップページにはog:typeをwebsiteで
クーポン詳細にはog:typeをarticleで設定ですね。

    このエントリーをはてなブックマークに追加

zoomeの動画をタグで検索

最近のゲーム動画はやっぱりzoomeですよね。
画面サイズ大きいし、容量も大きいし、ビットレートも高い。
ニコ動からもだいぶ流れてきてるように思います。
というわけで、zoomeのタグ検索をPHPで。

require_once 'XML/RPC.php';

$arr = array('tagname' => 'ゲーム',
             'offset' => 0,
             'length' => 10
             );
$params  = new XML_RPC_Value(array(
                                   'tagname' => new XML_RPC_Value($arr['tagname'], 'string'),
                                   'offset' => new XML_RPC_Value($arr['offset'], 'i4'),
                                   'length' => new XML_RPC_Value($arr['length'], 'i4'),
                                   ), "struct");

$msg = new XML_RPC_Message('zoome.tagSearch', array($params));
$cli = new XML_RPC_Client('/xmlrpc', 'up.zoome.jp', 80);
$res = $cli->send($msg);

if (!$res->faultCode()) {
  $data = XML_RPC_decode($res->value());
  /* "Total:". $data['count']; */
  foreach($data['list'] as $key => $val) {
    $zoome = array(
                   'zoomeid' => $val['zoomeid'],
                   'diaryid' => $val['diaryid'],
                   'title' => $val['title'],
                   'url' => $val['diaryurl'],
                   'body' => $val['body'],
                   );
  }
} else {
  exit('CODE:'.$res->faultCode(). ':'. $res->faultString());
}


欲をいえばソートがほしいですね。
デフォルトで新着順だと思うんですけど、再生数のソートくらいはほしいかなー。
他にも投稿とかのメソッドがあるので公式ドキュメントを参照してみてください。

参考:zoome API(XML-RPC)ドキュメント
    このエントリーをはてなブックマークに追加

OpenIDでログインして、ニックネームを登録してもらうような画面でのバリデートメモ。

usersテーブルのnicknameカラムにバリデートをかけたい。


チェック項目は3つ。

  • ユニークなニックネームか
  • 文字数制限内か
  • 空白のみではないか

空白のみの入力をfunction notSpaceでチェック。

APP/model/user.php

<?php
class User extends AppModel {
    var $name = 'User';
    var $validate = array(
        'nickname' => array(
                            'notSpace' => array(
                                                'rule' => array('notSpace'),
                                                'message' => '空白のみは登録できません。'
                                                ),
                            'between' => array(
                                               'rule' => array('between', 1, 45),
                                               'message' => '1文字以上入力してください。(全角15文字まで)'
                                               ),
                            'isUnique' => array(
                                                'rule' => 'isUnique',
                                                'message' => 'すでに登録されています。'
                                                ),
                            ),
        );
    function notSpace($field=array()) {
      foreach($field as $name => $value){
        if (preg_match("/^( | )+$/", $value)) {
          return false;
        } else {
          return true;
        }
      }
    }
}
?>
    このエントリーをはてなブックマークに追加

前回(アメブロのAtomAPIが倒せない)の記事から数カ月。。
アクセス解析見てもやっぱり同じように困ってる人がいるみたい。

というわけで、久しぶりに調べ直した。
前回のソースと違う部分をピックアップ。
やっぱりWSSE認証がポイントでした。これさえ通ればあとはすんなり。


$nonce = sha1(md5(time()));


ランダム文字生成だから何でもいいとは思うけど。


$pass_digest = base64_encode(pack('H*', sha1($nonce.$created.strtolower(md5($password)))));


ここが全然逆だった。
大文字にするんじゃなくて小文字にする。


あとはgetで記事情報を取得し、その結果からpostするURLをとって記事を投げるみたい。

今回使用したソース。
続きを読む
    このエントリーをはてなブックマークに追加

とりあえずテストでモバイルサイトで作ってみようと、
既存のぼくが管理してるサイト(ネトゲ)のモバイル版作ってみた。

とくにモバイル用に機能追加などはしておらず、
デイリーだったランキングを数時間単位にして表示したくらい。
あとPC版はZendFrameworkで作ってるけど、モバイル版はCakePHPってところが主な違い。

2010年1月18日にPCサイトでモバイル版の告知。
約1週間の計測。

■モバイル版


moPV

moUU



■同期間のPC版。


pcPV

pcUU



■考察


UUをみると、このままでもPC版の1/10くらいのユーザは来そうな感じ。
まーでも、このサイトの9割近くのユーザがネトゲヲタなのでPCの方が使い勝手とか親和性が高いってことか。
ゲームの裏画面でチェックすることが多そうだし。
残りの1割のリア充(笑)が移動中とかに見てるって考えると、この結果は非常に納得。

あ、よく見るとモバイル版のPVが1/23、1/24とそこだけ凹んでる。
この2日間は土日・・・。

要するに外には出ずに、家でずっとネトゲっていうパターンね。


なんかここまで考察に確信が持てるってなかなかないよね。。
    このエントリーをはてなブックマークに追加

ちょっと調べることがあったので、メモとして残しておきます。

日本語のtweetの抽出方法はいろいろあるとおもうんだけど、
今回はフォロワー取得APIでユーザの最新のステータスが取れることから、
特定の有名人のフォロワーのtweetを集めて平均値を出してみようかと。

たぶん統計学的にどのくらいのtweetの平均値をとれば統計として信用できる値とかってあるはずよね。。
習ったような習わなかったような気がするけど、とりあえず適当な数とってみました。

ターゲットとした有名人は勝間さん。
勝間さんのフォロワーをAPIで取得して平均値をだしました。

ちょっと古いAPIの仕様見てたらpage=1とかでページ数指定するって書いてたんだけど、公式のドキュメント見たらcursorになってた。
無駄にハマった。

初期値はcursor=-1を設定して、レスポンスにnext_cursorが返ってくるので次のページを見るときはその値を設定する。
(要するにcursor=1,2,3とかじゃないみたい。)

下に書いてある簡単なバッチ作ってみたところ、
対象Tweet6587tweet
トータル文字数234153文字
平均Tweet文字数35.5文字
以上のような結果に。
*)RTで始まってるtweetは除きました。

何度かやってみてもだいたい30文字台だったので、
日本人は30〜40文字でつぶやいているんでしょう、たぶん。

tweetLength.php

<?php
$username = "USERNAME";
$password = "PASSWORD";
$target = 'kazuyo_k';

$tweet=0;
$length=0;
$cursor = -1;
for ($i=0;$i<150;$i++) {
  if ($cursor == 0) break 1;

  $url = sprintf("http://twitter.com/statuses/followers.json?cursor=%d&screen_name=%s",$cursor,$target);
  $json = request($url,$username,$password);
  if (!is_array($json->users)) continue 1;
  foreach ($json->users as $key => $val) {
    if(isset($val->status)) {
      //RTで始まるtweetは飛ばす
      if(preg_match("/^RT(.+)/",$val->status->text)) continue 1;

      if (mb_detect_encoding($val->status->text) != 'UTF-8') continue 1;
      $tweet++;
      $length+=mb_strlen($val->status->text, 'utf-8');
    }
  }
  $cursor = $json->next_cursor;
}
echo "tweet  : " . $tweet . "\n";
echo "length : " . $length . "\n";
echo "avg    : " . $length / $tweet . "\n";

function request($host='',$username='',$password=''){
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $host);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
  curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  $result = curl_exec($ch);
  curl_close($ch);

  return json_decode($result);
}
?>
    このエントリーをはてなブックマークに追加



P1000256


ちょうど引越しの時に読まなくなった本を売ろうと思ってたので、
ついでに手順をnanapiに書きました。
写真は自分で引越しの時に撮ったものです。

1回も読んでない本もあったので、来年は読む分だけ買うようにします。。

トータル1万円弱になったので、いらないもの売った割にはいいお値段でした。
    このエントリーをはてなブックマークに追加


地味な特技だと思っていたので、とりあえず書いてみよう的なノリだったんですが、
予想外に見てもらえてたのでブログにも残しておきます。

実際のコツについては、nanapiをご覧ください。

今回はせっかくなので雑談でも。

なぜオセロにハマったのか。


初めてインターネットをしたのが高校時代。
田舎にしては早いほうだったと思います。
回りにやってる人は誰もいませんでした。
インターネットにあったものといえば、チャット、BBS、そしてヤフーゲーム。

ルールも簡単だし、ネットで誰かと対戦できるのが楽しそうでオセロをやるようになりました。
ぼくは自分が好きなことに対しては負けず嫌いな性格をしてるので、案の定なぜ負けるのかが気になりだします。
そしてちょうどこのタイミングで田舎にもテレホがやってきました。
ここから夜な夜なオセロの研究が始まったのがきっかけです。


リアル進出


大学受験のためしばらくオセロから離れていた時期がありましたが、大学が落ち着いてふと思い出したのをきっかけに再熱。
ある日、対戦後に「どちらにお住まいですか?」と聞かれて答えたところ、「定例会あるから来てみない?」と誘われたのをきっかけに実際のオセロ界へ進出していきました。


全国大会


運よくその年の予選ブロックを勝ち抜き、全国大会出場となったわけなんですが、
幸か不幸かこの年はネットとリアルのオセロ界の融合が始まった年のような気がします。
ヤフーゲームでの有名人が次々とリアルの大会に出場し上位に食い込んでいきました。
ネット上での強さのインフレはすさまじい勢いでした。
それほど現行のオセロ界とネット界での強さにおいて、格差があった時代だと思います。


2段獲得


おそらく前例のないことだと思うんですが、賞金がでる大会が開催されました。
この大会の予選、ぼくのオセロ史上最高段位との対戦がありました。
当時七段の相手に、中盤でほぼ詰まされ後はされるがまま。。
負けがその一戦のみだったためベスト4に入り2段に昇格。


以上の出来事が2001年までのことです。
それ以降オセロ界から離れたため、実に8年近くのブランクがあります。
正直今は1級相手でも勝てるかわかりませんw

このnanapiのレシピを書くにあたり、久しぶりにオセロ界隈のニュースやサイトを見てたら懐かしくなっちゃってこのエントリー書いた次第です。

nanapiに書いてない注意点


今回書いたコツは、あくまでも初心者相手にしたときのコツです。
正直なところ上級者相手ですと、このくらいのコツを覚えたくらいの相手がいちばんやりやすくなるので、
ヤフーゲームやハンゲームでぜんぜん勝てないじゃん!と言われても責任とれませんw



    このエントリーをはてなブックマークに追加

なんかそのうち使いそうなのでコピペ用メモ。
jQueryを使ってjsonpでデータ取得。
とりあえずライブラリにして使うところで読み込むかんじに。

bitlyApi.js

BitlyClient = function() {
  this._initialize();
}
$.extend(BitlyClient.prototype, {
  _initialize: function(login, apikey, version) {
    this.login = login || "LOGIN";
    this.apikey = apikey || "APIKEY";
    this.version = version || "2.0.1";
  },
  shorten: function(longUrl, target) {
    var result;
    $.ajax({
      type: "GET",
      url: "http://api.bit.ly/shorten",
      data: {
        "login": this.login,
        "apiKey": this.apikey,
        "version": this.version,
        "longUrl": longUrl
      },
      dataType: "jsonp",
      success: function(data) {
        for (var r in data.results) {
          result = data.results[r];
          break;
        };
        target.text(result.shortUrl);
      }
    });
  },
  expand: function(shortUrl, target) {
    var result;
    $.ajax({
      type: "GET",
      url: "http://api.bit.ly/expand",
      data: {
        "login": this.login,
        "apiKey": this.apikey,
        "version": this.version,
        "shortUrl": shortUrl
      },
      dataType: "jsonp",
      success: function(data) {
        for (var r in data.results) {
          result = data.results[r];
          break;
        };
        target.text(result.longUrl);
      }
    });
  }
});
var Bitly = new BitlyClient();


URLとターゲット渡して出力するライブラリなので使う用途に合わせて。
とりあえずURL入力して出力する例。

html

<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></script>
<script src="bitlyApi.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
  $('input[id^=bitly]').click(function() {
    eval("Bitly."+$(this).val()+"($('input#url').val(), $('div#bitly-result'))");
  });
});
</script>
</head>
<body>
<input type="text" id="url" /><input type="button" value="shorten" id="bitly-shorten" /><input type="button" value="expand" id="bitly-expand" />
<div id="bitly-result"></div>

    このエントリーをはてなブックマークに追加

各ページによって読み込むjsを変えたいときに。
簡単なことだけど、ふと忘れそうなので。

/path/to/app/webroot/js/foo.jsを読み込みたい場合。

・controller

コントローラではヘルパーの呼び出し。
<?php
class HogeController extends AppController {
  var $name = 'Hoge';
  var $helpers = array('Javascript');

  public function index() {
  }


・view

ビューではaddScriptで<head>タグ内に記述される。
($scripts_for_layoutの部分)
<?php $this->addScript($javascript->link('foo')); ?>

その場所に出力したい場合はそのまま
<?php echo $javascript->link('foo'); ?>

・layout

レイアウトはとりあえずこんなかんじで$scripts_for_layoutを書く。
<!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" />
<?php echo $scripts_for_layout; ?>
</head>
<body>
    このエントリーをはてなブックマークに追加

どうTwitterを楽しむべきか、
なにが楽しいのかいまいちよくわからんので
自分で興味が持てるようにAPI使ってサイト作りました。
あとCakePHPの練習も兼ねて。

いちおうECサイト担当っぽく「欲しい」をキーワードに集めてみました。

とりあえずぶっぱなしぎみで作ったので中身ごり押しな感じです。


欲望なう

欲望なう

キャッシュだったり、もうちょっとリアルタイム性だしたりと改善点は多々あると思うのでぼちぼち作ります。
    このエントリーをはてなブックマークに追加

前回のlivedoorBlogにAtomAPIで投稿に引き続きアメブロもやっちゃうぜーと思ったらハマった。
同じAtomAPIだしコード同じで行けるだろうと思ったら違うらしい。

いろいろ調べてみるとWSSE認証が独自ぽい。
でもいくら調べても同じように困ってる人はいても、解決してる人が見つからない。。

追記)倒しました!!(2010-02-03)  >>こちらの記事へ。



これまで調べてlivedoorBlogと違うと思われる部分がpass_digestの作成部分。

livedoorBlog
$pass_digest = base64_encode(pack('H*', sha1($nonce.$created.$password)));

アメブロ
$pass_digest = base64_encode(pack('H*', sha1($nonce.$created.strtoupper(md5($password)))));

パスワードをmd5したあとに大文字化するらしいとの情報。
デマかホントかわかりません。。

返ってくるレスポンスは
CurlResponse Object
(
    [body] => 
X-WSSE authentication required
    [headers] => Array
        (
            [Http-Version] => 1.1
            [Status-Code] => 401
            [Status] => 401 Unauthorized
            [Set-Cookie] => BIGipServerPool_comment=3395622060.20480.0000; expires=Sat, 17-Oct-2009 01:32:13 GMT; path=/
            [Date] => Fri, 16 Oct 2009 02:18:33 GMT
            [Server] => Apache
            [Content-Length] => 83
            [Content-Type] => application/x.atom+xml
        )

)

だめだー、なんか違うとこで間違ってるのか・・・。
今回使用したコードは以下。

続きを読む
    このエントリーをはてなブックマークに追加

app/vendors/shells/以下にファイルを作成。

/path/to/app/vendors/shells/foo.php
<?php
class FooShell extends Shell {

  var $uses = array('Hoge');

  function bar(){
    $this->args[0]; /* コマンドラインからの引数 */
    /* 処理 */
  }
}
?>


cronで使用するケースが多いと思うので、下記のように-appにappディレクトリのパスを設定。
/path/to/cake/console/cake foo bar -app /path/to/app/
    このエントリーをはてなブックマークに追加

前回の記事に引き続きAtomAPI用。
PHPとcURL使用。

<?php
require_once 'cURL.php';

$atomapi_url = "http://cms.blog.livedoor.com/atom/";
$livedoor_id = "your livedoor id"; /* livedoorID */
$password = "your password"; /* パスワード */
$category = "1"; /* カテゴリ */
$title = "sample"; /* 記事タイトル */
$text = "content"; /* 記事本文 */

$created = date('Y-m-d\TH:i:s\Z');
$nonce = pack('H*', sha1(md5(time())));
$pass_digest = base64_encode(pack('H*', sha1($nonce.$created.$password)));
$wsse =
  'UsernameToken Username="'.$livedoor_id.'", '.
  'PasswordDigest="'.$pass_digest.'", '.
  'Nonce="'.base64_encode($nonce).'", '.
  'Created="'.$created.'"';

$text64= base64_encode($text);

$rawdata =
  '<?xml version="1.0"?>'.
  '<entry xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">'.
  '<title type="text/html" mode="escaped">'.$title.'</title>'.
  '<dc:subject type="text/html" mode="escaped">'.$category.'</dc:subject>'.
  '<content type="application/xhtml+xml" mode="base64">'.$text64.'</content>'.
  '</entry>';

$headers = array(
              "X-WSSE" => $wsse,
              "Content-Type" => "application/x.atom+xml",
              "Cache-Control" => "no-cache",
              );

$curl = new Curl();
$curl->headers = $headers;
$curl->options = array("POSTFIELDS" => $rawdata);
$res = $curl->post($atomapi_url);
?>
    このエントリーをはてなブックマークに追加

cakePHPを使ってみる。
PHPカンファレンス2009(前回の記事)でも使用者が一番多かったフレームワーク。
MVCのフレームワーク使ったことある人にはわかりやすいディレクトリ構成。

慣れるまでのメモをつらつら。

基本構成(post_typesテーブルの場合)

コントローラURL: (複数)/post_types/メソッド名/パラメータ
コントローラ: (複数)PostTypesController (extends AppController)
モデル: (単数)PostType (extends AppModel)
ビュー: (複数)app/views/post_types/各メソッド.ctp

モデル

  • テーブルの主キーは、"id"というフィールドで、primary、auto_increment で作るのが基本。
  • "モデル名_id"というフィールドを作るだけで、他のテーブルモデルへの外部キーとして設定。 例:users テーブルの中に、 "post_id" を作成  ⇒ posts テーブルの id を参照。 "post_type_id" を作成 ⇒ post_types テーブルの id を参照。
  • "created" (DATETIME)というフィールドを作成するだけで、新しいデータが入ると時間が自動的に記録。
  • "modified" (DATETIME)というフィールドを作成するだけで、データが更新されるたびに時間が自動的に記録。
  • "updated" (DATETIME)というフィールドを作成するだけで、データが更新されるたびに時間が自動的に記録。
  • "title"というフィールドを作ると、そのページの標準のタイトル名として利用される。
  • "name"や"title" というフィールドを作ると、外部から参照された場合に(指定がなければ)その値がセレクトボックスなどの「選択項目名」として自動的に利用される。(言い換えると、generateList()の引数が指定されていない場合にはこのように動作するということになります。)どちらもなければ、idの値がインデックスと項目名の両方に入ります。(このあたりの処理は、model_php4.phpと model_php5.php に書かれています。)

ビューを出力する必要がないメソッドの場合

<?php
$this->autoRender = false;
?>
あれ、絶対テーブル必要なの・・・!?って思ってしまったから これもメモ。

テーブル未使用モデル、モデル未使用コントローラ

・DBを使用しないモデル⇒$useTable = false;
・モデルを使用しないコントローラ⇒ var $uses = array();、または var $uses = nullでOK。
    このエントリーをはてなブックマークに追加

TwitterAPIに検索キーワードを投げて表示するだけのサンプル。

リクエストパラメータの一例。
詳細はこちらを参照
q検索キーワード
callbackコールバック関数を指定したいときは設定
lang検索対象の言語
rpp取得する検索結果数
page取得ページ


レスポンスデータはこんなかんじ。
created_at発言日時
from_userユーザID
profile_image_urlユーザのプロフィール画像
text発言

続いてソースとサンプル。
続きを読む
    このエントリーをはてなブックマークに追加

各サービスのエンドポイントメモ。
とりあえずXML-RPCに対応してそうなブログの一覧。
サービスhostpath
FC2blog.fc2.com/xmlrpc.php
gooblog.goo.ne.jp/xmlrpc.php
seesaablog.seesaa.jp/rpc
jugenYOURID.jugem.jp/xmlrpc.php
ココログapp.f.cocolog-nifty.com/t/api
ninjaYOURID.blog.shinobi.jp/XMLRPC/


XMLRPC::Liteモジュールを利用してPerlでの実装。
続きを読む
    このエントリーをはてなブックマークに追加

参照元: 21 Really Useful & Handy PHP Code Snippets
if(!emptyempty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'){  
 //If AJAX Request Then  
}else{  
 //something else  
} 
    このエントリーをはてなブックマークに追加

ふとjsonpを使おうと思ったんだけど たぶんぼくのドキュメント読解力が足りないせいで無駄に時間かかった。
忘れないようにメモ。

$.ajax({
    type: "GET",
    url: "http://sample.com/",
    data: {
      "param": "hogehoge",
    },
    dataType: "jsonp",
    jsonp: "callBack",
    success: function(data) {
      /* success */
    },
    complete: function(data) {
      /* complete */
    }
});

dataTypeをjsonpにするとjQueryがcallbackパラメータを自動で入れてくれる。

callback=jsonp1234391216435みたいな。

jsonpオプションを付けると↑のcallbackの部分を指定した名前にできる。
サーバ側のAPIによってcallbackのパラメータ名が決められている場合があるのでそういうときに使う。


callback関数を指定したければdataオプションに書く。

data: { "callback" : "hoge" }

callback関数を指定しないとjQuery内部で以下のコードが実行される。
window[ 'jsonp1242391216435' ] = function(tmp){
  data = tmp;
  success();
  complete();
  // ガベージコレクト処理
  window[ jsonp ] = undefined;
  try{ delete window[ jsonp ]; } catch(e){}
  if ( head )
  head.removeChild( script );
}


サーバ側はこんなのでとりあえずデータ返してみた。
返すデータはお好きに。
<?php
$data = array("a" => "hoge", "b" => "foo", "c" => "bar");
$json = json_encode($data);
$callback = htmlspecialchars(strip_tags($_GET["callback"]));
echo $callback . "(" . $json . ")";
?>
    このエントリーをはてなブックマークに追加

このページのトップヘ